diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e78c245d..e045b811 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -46,10 +46,11 @@ dependencies { implementation(libs.kakao.user) implementation(projects.feature.auth) - implementation(projects.feature.etc) + implementation(projects.feature.setting) implementation(projects.feature.matching) implementation(projects.feature.mypage) implementation(projects.core.designsystem) implementation(projects.core.navigation) + implementation(projects.core.commonUi) } \ No newline at end of file diff --git a/app/src/main/java/com/puzzle/piece/navigation/AppNavHost.kt b/app/src/main/java/com/puzzle/piece/navigation/AppNavHost.kt index 4364d591..32b4ea71 100644 --- a/app/src/main/java/com/puzzle/piece/navigation/AppNavHost.kt +++ b/app/src/main/java/com/puzzle/piece/navigation/AppNavHost.kt @@ -5,7 +5,7 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import com.puzzle.auth.navigation.authNavGraph -import com.puzzle.etc.navigation.etcScreen +import com.puzzle.setting.navigation.settingScreen import com.puzzle.matching.navigation.matchingNavGraph import com.puzzle.mypage.navigation.myPageScreen import com.puzzle.navigation.AuthGraph @@ -23,6 +23,6 @@ fun AppNavHost( authNavGraph() matchingNavGraph() myPageScreen() - etcScreen() + settingScreen() } } diff --git a/app/src/main/java/com/puzzle/piece/navigation/TopLevelDestinvation.kt b/app/src/main/java/com/puzzle/piece/navigation/TopLevelDestinvation.kt index ef734737..199a1ba3 100644 --- a/app/src/main/java/com/puzzle/piece/navigation/TopLevelDestinvation.kt +++ b/app/src/main/java/com/puzzle/piece/navigation/TopLevelDestinvation.kt @@ -1,41 +1,35 @@ package com.puzzle.piece.navigation -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Call -import androidx.compose.material.icons.outlined.Call -import androidx.compose.ui.graphics.vector.ImageVector -import com.puzzle.navigation.EtcRoute +import androidx.annotation.DrawableRes +import com.puzzle.navigation.SettingRoute import com.puzzle.navigation.MatchingGraphDest import com.puzzle.navigation.MyPageRoute +import com.puzzle.piece.R import kotlin.reflect.KClass enum class TopLevelDestination( - val selectedIcon: ImageVector, - val unselectedIcon: ImageVector, - val iconText: String, - val titleText: String, + @DrawableRes val iconDrawableId: Int, + val contentDescription: String, + val title: String, val route: KClass<*>, ) { - MATCHING( - selectedIcon = Icons.Filled.Call, - unselectedIcon = Icons.Outlined.Call, - iconText = "매칭", - titleText = "매칭", - route = MatchingGraphDest.MatchingRoute::class, - ), MY_PAGE( - selectedIcon = Icons.Filled.Call, - unselectedIcon = Icons.Outlined.Call, - iconText = "마이페이지", - titleText = "마이페이지", + iconDrawableId = R.drawable.ic_profile, + contentDescription = "프로필", + title = "프로필", route = MyPageRoute::class, ), - ETC( - selectedIcon = Icons.Filled.Call, - unselectedIcon = Icons.Outlined.Call, - iconText = "ETC", - titleText = "ETC", - route = EtcRoute::class, + MATCHING( + iconDrawableId = R.drawable.ic_profile, + contentDescription = "매칭", + title = "매칭", + route = MatchingGraphDest.MatchingRoute::class, + ), + SETTING( + iconDrawableId = R.drawable.ic_setting, + contentDescription = "설정", + title = "설정", + route = SettingRoute::class, ); companion object { diff --git a/app/src/main/java/com/puzzle/piece/ui/App.kt b/app/src/main/java/com/puzzle/piece/ui/App.kt index 7174510e..f08eab2a 100644 --- a/app/src/main/java/com/puzzle/piece/ui/App.kt +++ b/app/src/main/java/com/puzzle/piece/ui/App.kt @@ -1,25 +1,45 @@ +@file:OptIn(ExperimentalMaterial3Api::class) + package com.puzzle.piece.ui +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding -import androidx.compose.material.BottomNavigation -import androidx.compose.material.BottomNavigationItem +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FabPosition +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.FloatingActionButtonDefaults.bottomAppBarFabElevation import androidx.compose.material3.Icon +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp import androidx.navigation.NavDestination import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavHostController import androidx.navigation.compose.currentBackStackEntryAsState +import com.puzzle.common.ui.NoRippleInteractionSource +import com.puzzle.designsystem.foundation.PieceTheme import com.puzzle.navigation.AuthGraph -import com.puzzle.navigation.EtcRoute import com.puzzle.navigation.MatchingGraph import com.puzzle.navigation.MatchingGraphDest.MatchingDetailRoute import com.puzzle.navigation.MyPageRoute import com.puzzle.navigation.Route +import com.puzzle.navigation.SettingRoute +import com.puzzle.piece.R import com.puzzle.piece.navigation.AppNavHost import com.puzzle.piece.navigation.TopLevelDestination import kotlin.reflect.KClass @@ -42,7 +62,25 @@ fun App( navigateToTopLevelDestination = navigateToTopLevelDestination, ) } - } + }, + floatingActionButton = { + if (currentDestination?.shouldHideBottomNavigation() == false) { + FloatingActionButton( + onClick = { navigateToTopLevelDestination(MatchingGraph) }, + containerColor = PieceTheme.colors.white, + shape = CircleShape, + elevation = bottomAppBarFabElevation(), + modifier = Modifier.offset(y = 84.dp), + ) { + Image( + painter = painterResource(R.drawable.ic_matching), + contentDescription = null, + modifier = Modifier.size(80.dp), + ) + } + } + }, + floatingActionButtonPosition = FabPosition.Center, ) { innerPadding -> val contentModifier = modifier.padding(innerPadding) @@ -58,24 +96,51 @@ private fun AppBottomBar( currentDestination: NavDestination?, navigateToTopLevelDestination: (Route) -> Unit, ) { - BottomNavigation( - modifier = Modifier.navigationBarsPadding() + NavigationBar( + containerColor = PieceTheme.colors.white, + modifier = Modifier + .navigationBarsPadding() + .height(68.dp), ) { TopLevelDestination.topLevelDestinations.forEach { topLevelRoute -> - BottomNavigationItem( + NavigationBarItem( icon = { - Icon( - imageVector = topLevelRoute.selectedIcon, - contentDescription = topLevelRoute.name - ) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(top = 2.dp), + ) { + Icon( + painter = painterResource(topLevelRoute.iconDrawableId), + contentDescription = topLevelRoute.contentDescription, + modifier = Modifier.size(32.dp), + ) + + Text( + text = topLevelRoute.title, + style = PieceTheme.typography.captionM, + ) + } }, - label = { Text(topLevelRoute.name) }, + alwaysShowLabel = false, selected = currentDestination.isRouteInHierarchy(topLevelRoute.route), + colors = androidx.compose.material3.NavigationBarItemDefaults.colors( + selectedIconColor = PieceTheme.colors.primaryDefault, + unselectedIconColor = PieceTheme.colors.dark3, + selectedTextColor = PieceTheme.colors.primaryDefault, + unselectedTextColor = PieceTheme.colors.dark3, + indicatorColor = Color.Transparent, + ), + interactionSource = remember { NoRippleInteractionSource() }, onClick = { when (topLevelRoute) { - TopLevelDestination.MATCHING -> navigateToTopLevelDestination(MatchingGraph) + TopLevelDestination.MATCHING -> navigateToTopLevelDestination( + MatchingGraph + ) + TopLevelDestination.MY_PAGE -> navigateToTopLevelDestination(MyPageRoute) - TopLevelDestination.ETC -> navigateToTopLevelDestination(EtcRoute) + TopLevelDestination.SETTING -> navigateToTopLevelDestination( + SettingRoute + ) } }, ) @@ -85,21 +150,11 @@ private fun AppBottomBar( private val HIDDEN_BOTTOM_NAV_ROUTES = setOf( AuthGraph::class.qualifiedName, - MatchingDetailRoute::class.qualifiedName + MatchingDetailRoute::class.qualifiedName, ) -/** - * 현재 목적지가 바텀 네비게이션이 보여지지 않는 화면인지 확인하는 메서드 - */ private fun NavDestination?.shouldHideBottomNavigation(): Boolean = - this?.hierarchy?.any { destination -> - destination.route in HIDDEN_BOTTOM_NAV_ROUTES - } ?: false + this?.hierarchy?.any { destination -> destination.route in HIDDEN_BOTTOM_NAV_ROUTES } ?: false -/** - * 현재 목적지가 TopLevelDestination 라우트에 속하는지 확인하는 메서드 - */ private fun NavDestination?.isRouteInHierarchy(route: KClass<*>): Boolean = - this?.hierarchy?.any { - it.hasRoute(route) - } ?: false \ No newline at end of file + this?.hierarchy?.any { it.hasRoute(route) } == true diff --git a/app/src/main/res/drawable/ic_matching.xml b/app/src/main/res/drawable/ic_matching.xml new file mode 100644 index 00000000..46acaef7 --- /dev/null +++ b/app/src/main/res/drawable/ic_matching.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_profile.xml b/app/src/main/res/drawable/ic_profile.xml new file mode 100644 index 00000000..bc255864 --- /dev/null +++ b/app/src/main/res/drawable/ic_profile.xml @@ -0,0 +1,33 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_setting.xml b/app/src/main/res/drawable/ic_setting.xml new file mode 100644 index 00000000..3371d1cd --- /dev/null +++ b/app/src/main/res/drawable/ic_setting.xml @@ -0,0 +1,16 @@ + + + + diff --git a/build-logic/src/main/java/com/puzzle/build/logic/configure/AndroidComposes.kt b/build-logic/src/main/java/com/puzzle/build/logic/configure/AndroidComposes.kt index 51cba50f..722cd11a 100644 --- a/build-logic/src/main/java/com/puzzle/build/logic/configure/AndroidComposes.kt +++ b/build-logic/src/main/java/com/puzzle/build/logic/configure/AndroidComposes.kt @@ -14,7 +14,7 @@ internal fun Project.configureAndroidCompose() { androidExtension.apply { composeOptions { - kotlinCompilerExtensionVersion = "1.3.0" + kotlinCompilerExtensionVersion = "1.5.15" } buildFeatures.apply { diff --git a/core/common-ui/src/main/java/com/puzzle/common/ui/InteractionSource.kt b/core/common-ui/src/main/java/com/puzzle/common/ui/InteractionSource.kt new file mode 100644 index 00000000..6d276386 --- /dev/null +++ b/core/common-ui/src/main/java/com/puzzle/common/ui/InteractionSource.kt @@ -0,0 +1,14 @@ +package com.puzzle.common.ui + +import androidx.compose.foundation.interaction.Interaction +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.runtime.Immutable +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +@Immutable +class NoRippleInteractionSource : MutableInteractionSource { + override suspend fun emit(interaction: Interaction) {} + override val interactions: Flow = emptyFlow() + override fun tryEmit(interaction: Interaction): Boolean = true +} diff --git a/core/navigation/src/main/java/com/puzzle/navigation/Route.kt b/core/navigation/src/main/java/com/puzzle/navigation/Route.kt index 20da3740..a2984cc5 100644 --- a/core/navigation/src/main/java/com/puzzle/navigation/Route.kt +++ b/core/navigation/src/main/java/com/puzzle/navigation/Route.kt @@ -13,7 +13,7 @@ sealed class AuthGraphDest : Route { } @Serializable -data object EtcRoute : Route +data object SettingRoute : Route @Serializable data object MatchingGraph : Route diff --git a/feature/etc/src/main/java/com/puzzle/etc/navigation/EtcNavigation.kt b/feature/etc/src/main/java/com/puzzle/etc/navigation/EtcNavigation.kt deleted file mode 100644 index 9dc4db8b..00000000 --- a/feature/etc/src/main/java/com/puzzle/etc/navigation/EtcNavigation.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.puzzle.etc.navigation - -import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable -import com.puzzle.etc.EtcRoute -import com.puzzle.navigation.EtcRoute - -fun NavGraphBuilder.etcScreen() { - composable { - EtcRoute() - } -} diff --git a/feature/matching/src/main/java/com/puzzle/matching/MatchingScreen.kt b/feature/matching/src/main/java/com/puzzle/matching/MatchingScreen.kt index 5148a701..bc597062 100644 --- a/feature/matching/src/main/java/com/puzzle/matching/MatchingScreen.kt +++ b/feature/matching/src/main/java/com/puzzle/matching/MatchingScreen.kt @@ -275,4 +275,4 @@ private fun PreviewMatchingScreen() { navigateToMatchingDetail = {}, ) } -} \ No newline at end of file +} diff --git a/feature/etc/.gitignore b/feature/setting/.gitignore similarity index 100% rename from feature/etc/.gitignore rename to feature/setting/.gitignore diff --git a/feature/etc/build.gradle.kts b/feature/setting/build.gradle.kts similarity index 60% rename from feature/etc/build.gradle.kts rename to feature/setting/build.gradle.kts index 4b387bc2..5d23cc98 100644 --- a/feature/etc/build.gradle.kts +++ b/feature/setting/build.gradle.kts @@ -3,5 +3,5 @@ plugins { } android { - namespace = "com.puzzle.etc" + namespace = "com.puzzle.setting" } diff --git a/feature/etc/consumer-rules.pro b/feature/setting/consumer-rules.pro similarity index 100% rename from feature/etc/consumer-rules.pro rename to feature/setting/consumer-rules.pro diff --git a/feature/etc/proguard-rules.pro b/feature/setting/proguard-rules.pro similarity index 100% rename from feature/etc/proguard-rules.pro rename to feature/setting/proguard-rules.pro diff --git a/feature/etc/src/androidTest/java/com/puzzle/etc/ExampleInstrumentedTest.kt b/feature/setting/src/androidTest/java/com/puzzle/setting/ExampleInstrumentedTest.kt similarity index 95% rename from feature/etc/src/androidTest/java/com/puzzle/etc/ExampleInstrumentedTest.kt rename to feature/setting/src/androidTest/java/com/puzzle/setting/ExampleInstrumentedTest.kt index 8ba53323..8d6f6fc1 100644 --- a/feature/etc/src/androidTest/java/com/puzzle/etc/ExampleInstrumentedTest.kt +++ b/feature/setting/src/androidTest/java/com/puzzle/setting/ExampleInstrumentedTest.kt @@ -1,4 +1,4 @@ -package com.puzzle.etc +package com.puzzle.setting import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry diff --git a/feature/etc/src/main/AndroidManifest.xml b/feature/setting/src/main/AndroidManifest.xml similarity index 100% rename from feature/etc/src/main/AndroidManifest.xml rename to feature/setting/src/main/AndroidManifest.xml diff --git a/feature/etc/src/main/java/com/puzzle/etc/EtcScreen.kt b/feature/setting/src/main/java/com/puzzle/setting/SettingScreen.kt similarity index 77% rename from feature/etc/src/main/java/com/puzzle/etc/EtcScreen.kt rename to feature/setting/src/main/java/com/puzzle/setting/SettingScreen.kt index b5ee9317..477492f1 100644 --- a/feature/etc/src/main/java/com/puzzle/etc/EtcScreen.kt +++ b/feature/setting/src/main/java/com/puzzle/setting/SettingScreen.kt @@ -1,4 +1,4 @@ -package com.puzzle.etc +package com.puzzle.setting import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize @@ -9,14 +9,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.sp @Composable -fun EtcRoute( +fun SettingRoute( modifier: Modifier = Modifier, ) { Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Text(text = "EtcRoute", fontSize = 30.sp) + Text(text = "SettingRoute", fontSize = 30.sp) } } @Composable -internal fun EtcScreen() { -} \ No newline at end of file +internal fun SettingScreen() { +} diff --git a/feature/setting/src/main/java/com/puzzle/setting/navigation/SettingNavigation.kt b/feature/setting/src/main/java/com/puzzle/setting/navigation/SettingNavigation.kt new file mode 100644 index 00000000..6b047673 --- /dev/null +++ b/feature/setting/src/main/java/com/puzzle/setting/navigation/SettingNavigation.kt @@ -0,0 +1,12 @@ +package com.puzzle.setting.navigation + +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import com.puzzle.setting.SettingRoute +import com.puzzle.navigation.SettingRoute + +fun NavGraphBuilder.settingScreen() { + composable { + SettingRoute() + } +} diff --git a/feature/etc/src/test/java/com/puzzle/etc/ExampleUnitTest.kt b/feature/setting/src/test/java/com/puzzle/setting/ExampleUnitTest.kt similarity index 92% rename from feature/etc/src/test/java/com/puzzle/etc/ExampleUnitTest.kt rename to feature/setting/src/test/java/com/puzzle/setting/ExampleUnitTest.kt index 8bca67f4..da9fd3f5 100644 --- a/feature/etc/src/test/java/com/puzzle/etc/ExampleUnitTest.kt +++ b/feature/setting/src/test/java/com/puzzle/setting/ExampleUnitTest.kt @@ -1,4 +1,4 @@ -package com.puzzle.etc +package com.puzzle.setting import org.junit.Assert.assertEquals import org.junit.Test diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0e6e3bef..ac04860f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] ## Android gradle plugin -androidGradlePlugin = "8.6.1" +androidGradlePlugin = "8.7.2" ## AndroidX # https://developer.android.com/jetpack/androidx/releases/core @@ -24,13 +24,9 @@ androidxSplashscreen = "1.0.1" ## Compose # https://developer.android.com/develop/ui/compose/bom/bom-mapping -androidxComposeBom = "2024.10.01" +androidxComposeBom = "2024.12.01" # https://developer.android.com/jetpack/androidx/releases/navigation androidxComposeNavigation = "2.8.4" -# https://developer.android.com/jetpack/androidx/releases/compose-material -androidxComposeMaterial = "1.7.4" -# https://developer.android.com/jetpack/androidx/releases/compose-material3 -androidxComposeMaterial3 = "1.3.0" ## Amplitude # https://amplitude.com/docs/sdks/analytics/android @@ -120,8 +116,8 @@ androidx-lifecycle-runtimeCompose = { group = "androidx.lifecycle", name = "life androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" } -androidx-compose-material = { group = "androidx.compose.material", name = "material", version.ref = "androidxComposeMaterial" } -androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "androidxComposeMaterial3" } +androidx-compose-material = { group = "androidx.compose.material", name = "material" } +androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 04bb79af..bed14419 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -27,7 +27,7 @@ include(":app") include(":feature:auth") include(":feature:mypage") include(":feature:matching") -include(":feature:etc") +include(":feature:setting") include(":core:domain") include(":core:designsystem") include(":core:data")