-
Notifications
You must be signed in to change notification settings - Fork 0
[GAMSS-41] feat: Android 멀티모듈 기반 및 공통 아키텍처 구축 #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
4ee1dda
build: 라이브러리 카탈로그 및 빌드 설정 정비
seunghee17 53a5b74
chore: core 모듈을 common과 ui로 분리
seunghee17 0f6aa2a
chore: core-ktx 의존성 버전 카탈로그 추가
seunghee17 6b02c82
refactor: UseCase 패키지 경로 정리
seunghee17 0670c2c
chore: data 모듈 Android Library 설정 정리
seunghee17 8039435
chore: app 모듈 core 세부 모듈 의존성 반영
seunghee17 983c226
chore: core 모듈 종속성 삭제
seunghee17 4518635
feat: Navigation3 기반 네비게이션 상태 관리 추가
seunghee17 b4579d1
chore: feature:home, core:ui 모듈에 Compose·Navigation3 의존성 추가
seunghee17 663a1c1
feat: HomeScreen과 HomeKey를 feature:home 모듈로 이동
seunghee17 231b1ae
feat: core:ui에 GamssBottomBar 컴포넌트 추가
seunghee17 71efd55
feat: 하단 네비게이션 바를 NavigationState·Navigator와 연결
seunghee17 a42d5e1
chore: settings.gradle에 feature:home 모듈 등록
seunghee17 d8cb091
chore: .gitignore에 IDE 빌드 산출물(bin/) 추가
seunghee17 9412bb2
feat: Auth 인증 흐름 mock 구조 추가 (network, repository, domain)
seunghee17 6d1cc64
feat: feature:home에 Hilt·Orbit 의존성 추가
seunghee17 ce90e83
feat: HomeScreen에 Orbit 상태관리 mock 구조 추가
seunghee17 c7bcbca
feat: app 모듈에 Firebase Crashlytics 종속성 추가
seunghee17 96083c0
feat: app 모듈에 Firebase Analytics 종속성 추가
seunghee17 6646689
chore: README 프로젝트 구조를 실제 멀티모듈 구성으로 갱신
seunghee17 7db6a49
Merge branch 'develop' into feature/setting-module
seunghee17 8d2568c
chore: CI에 google-services.json 시크릿 주입 스텝 추가
seunghee17 2b8c5d5
fix: develop 병합 후 발생한 detekt 위반 사항 수정
seunghee17 f2dfd2d
refactor: BottomBar 아이템 모델 파일 분리
seunghee17 a5fa87c
fix: develop 병합 후 발생한 detekt 위반 사항 수정
seunghee17 3d24487
fix: Kotlin JvmTarget import로 Gradle 설정 정리
seunghee17 a0e4c52
fix: domain 모듈 인증 응답 토큰 기본값을 null로 변경
seunghee17 9b1a439
fix: 네비게이션 엔트리 변환에서 불필요한 mutable 변환 제거
seunghee17 30f0195
fix: 네비게이션 뒤로가기 처리 책임을 Host로 이동
seunghee17 5df0448
refactor: BottomBarItem 상태와 동작 분리
seunghee17 88f1a45
feat: 채팅 목록, 달력 화면 추가
seunghee17 794f28d
refactor: core:common 모듈 kotlinJvm 전환
seunghee17 10c119c
fix: AuthRepository login 결과를 AppResult로 감싸기
seunghee17 dbaaaa5
feat: top-level 화면에서만 BottomBar 표시되도록 조건 추가
seunghee17 ce3e4cd
fix: feature:calendar, feature:chat 모듈 detekt 위반 수정
seunghee17 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,8 @@ | |
| .idea/ | ||
| .kotlin/ | ||
| build/ | ||
| bin/ | ||
| local.properties | ||
| */build/ | ||
| .worktree-* | ||
| app/google-services.json | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 0 additions & 20 deletions
20
app/src/main/kotlin/com/gamss/android/app/navigation/AppNavHost.kt
This file was deleted.
Oops, something went wrong.
53 changes: 53 additions & 0 deletions
53
app/src/main/kotlin/com/gamss/android/app/navigation/GamssNavHost.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| package com.gamss.android.app.navigation | ||
|
|
||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.material3.Scaffold | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.remember | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.navigation3.runtime.entryProvider | ||
| import androidx.navigation3.ui.NavDisplay | ||
| import com.gamss.android.core.ui.GamssBottomBar | ||
| import com.gamss.android.feature.calendar.CalendarScreen | ||
| import com.gamss.android.feature.calendar.navigation.CalendarKey | ||
| import com.gamss.android.feature.chat.ChattingListScreen | ||
| import com.gamss.android.feature.chat.navigation.ChatKey | ||
| import com.gamss.android.feature.home.HomeScreen | ||
| import com.gamss.android.feature.home.navigation.HomeKey | ||
|
|
||
| @Composable | ||
| fun GamssNavHost() { | ||
| val navigationState = rememberNavigationState( | ||
| startKey = HomeKey, | ||
| topLevelKeys = topLevelDestinationKeys, | ||
| ) | ||
| val navigator = remember(navigationState) { Navigator(navigationState) } | ||
|
|
||
| Scaffold( | ||
| bottomBar = { | ||
| if (navigationState.currentKey == navigationState.currentTopLevelKey) { | ||
| GamssBottomBar( | ||
| items = topLevelBottomBarItems, | ||
| selectedValue = navigationState.currentTopLevelKey, | ||
| onItemClick = navigator::navigate, | ||
| ) | ||
| } | ||
| }, | ||
| ) { innerPadding -> | ||
| NavDisplay( | ||
| modifier = Modifier.padding(innerPadding), | ||
| entries = navigationState.toEntries( | ||
| entryProvider = entryProvider { | ||
| entry<HomeKey> { HomeScreen() } | ||
| entry<ChatKey> { ChattingListScreen() } | ||
| entry<CalendarKey> { CalendarScreen() } | ||
| }, | ||
| ), | ||
| onBack = { | ||
| if (navigationState.canGoBack) { | ||
| navigator.goBack() | ||
| } | ||
| }, | ||
| ) | ||
| } | ||
| } |
90 changes: 90 additions & 0 deletions
90
app/src/main/kotlin/com/gamss/android/app/navigation/NavigationState.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package com.gamss.android.app.navigation | ||
|
|
||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.derivedStateOf | ||
| import androidx.compose.runtime.getValue | ||
| import androidx.compose.runtime.remember | ||
| import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator | ||
| import androidx.navigation3.runtime.NavBackStack | ||
| import androidx.navigation3.runtime.NavEntry | ||
| import androidx.navigation3.runtime.NavKey | ||
| import androidx.navigation3.runtime.rememberDecoratedNavEntries | ||
| import androidx.navigation3.runtime.rememberNavBackStack | ||
| import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator | ||
|
|
||
| /** | ||
| * GAMSS 메인 탭 네비게이션 상태를 생성한다. | ||
| * | ||
| * Navigation3의 back stack은 rememberSaveable 기반으로 동작하므로 화면 회전이나 | ||
| * 프로세스 재생성 이후에도 복원 가능한 key를 사용해야 한다. | ||
| * | ||
| * @param startKey 앱의 메인 영역에서 처음 보여줄 최상위 key | ||
| * @param topLevelKeys bottom bar에 연결되는 최상위 key 목록 | ||
| */ | ||
| @Composable | ||
| fun rememberNavigationState( | ||
| startKey: NavKey, | ||
| topLevelKeys: Set<NavKey>, | ||
| ): NavigationState { | ||
| val topLevelStack = rememberNavBackStack(startKey) | ||
| val subStacks = topLevelKeys.associateWith { key -> rememberNavBackStack(key) } | ||
|
|
||
| return remember(startKey, topLevelKeys) { | ||
| NavigationState( | ||
| startKey = startKey, | ||
| topLevelStack = topLevelStack, | ||
| subStacks = subStacks, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * bottom bar 기반 화면 전환을 위한 상태 홀더. | ||
| * | ||
| * topLevelStack은 사용자가 방문한 탭 순서를 저장하고, subStacks는 각 탭 내부의 상세 화면 | ||
| * stack을 따로 저장한다. 탭을 전환해도 탭의 상세 화면 흐름을 유지할 수 있다. | ||
| */ | ||
| class NavigationState( | ||
| val startKey: NavKey, | ||
| val topLevelStack: NavBackStack<NavKey>, | ||
| val subStacks: Map<NavKey, NavBackStack<NavKey>>, | ||
| ) { | ||
| val currentTopLevelKey: NavKey by derivedStateOf { topLevelStack.last() } | ||
|
|
||
| val topLevelKeys | ||
| get() = subStacks.keys | ||
|
|
||
| val currentSubStack: NavBackStack<NavKey> | ||
| get() = subStacks[currentTopLevelKey] | ||
| ?: error("현재 탭($currentTopLevelKey)에 해당하는 back stack이 없습니다.") | ||
|
|
||
| val currentKey: NavKey by derivedStateOf { currentSubStack.last() } | ||
|
|
||
| val canGoBack: Boolean by derivedStateOf { currentKey != startKey } | ||
| } | ||
|
|
||
| /** | ||
| * NavigationState를 NavDisplay에서 사용할 entry 목록으로 변환한다. | ||
| * | ||
| * 각 탭의 sub stack에 saveable state와 ViewModelStore decorator를 붙여서, 탭 전환 중에도 | ||
| * 화면 상태와 ViewModel 생명주기가 탭별 back stack에 맞게 유지되도록 한다. | ||
| */ | ||
| @Composable | ||
| fun NavigationState.toEntries( | ||
| entryProvider: (NavKey) -> NavEntry<NavKey>, | ||
| ): List<NavEntry<NavKey>> { | ||
| val decoratedEntries = subStacks.mapValues { (_, stack) -> | ||
| val decorators = listOf( | ||
| rememberSaveableStateHolderNavEntryDecorator<NavKey>(), | ||
| rememberViewModelStoreNavEntryDecorator<NavKey>(), | ||
| ) | ||
| rememberDecoratedNavEntries( | ||
| backStack = stack, | ||
| entryDecorators = decorators, | ||
| entryProvider = entryProvider, | ||
| ) | ||
| } | ||
|
|
||
| return topLevelStack | ||
|
soyeonLee126 marked this conversation as resolved.
|
||
| .flatMap { decoratedEntries[it] ?: emptyList() } | ||
| } | ||
75 changes: 75 additions & 0 deletions
75
app/src/main/kotlin/com/gamss/android/app/navigation/Navigator.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| package com.gamss.android.app.navigation | ||
|
|
||
| import androidx.navigation3.runtime.NavKey | ||
|
|
||
| /** | ||
| * NavigationState를 변경하는 앱 전용 navigator. | ||
| * | ||
| * GAMSS의 bottom bar 정책을 한 곳에 모아둔다. | ||
| * - 현재 탭을 다시 선택하면 해당 탭의 root 화면으로 이동 | ||
| * - 다른 탭을 선택하면 방문한 탭 순서를 topLevelStack에 기록 | ||
| * - 상세 화면은 현재 탭의 sub stack에 쌓되, 같은 key가 이미 있으면 마지막으로 이동 | ||
| */ | ||
| class Navigator(val state: NavigationState) { | ||
|
|
||
| /** | ||
| * 지정한 key로 이동한다. | ||
| * top-level key인지, 현재 탭인지, 상세 key인지에 따라 stack 갱신 규칙이 달라진다. | ||
| */ | ||
| fun navigate(key: NavKey) { | ||
| when (key) { | ||
| state.currentTopLevelKey -> clearSubStack() | ||
| in state.topLevelKeys -> goToTopLevel(key) | ||
| else -> goToKey(key) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 현재 위치에서 뒤로 이동한다. | ||
| * | ||
| * 현재 탭의 root 화면에서는 이전에 방문한 탭으로 돌아가고, 상세 화면에서는 현재 탭의 | ||
| * sub stack에서 한 단계 pop한다. | ||
| */ | ||
| fun goBack() { | ||
| when (state.currentKey) { | ||
| state.startKey -> Unit | ||
| state.currentTopLevelKey -> { | ||
| state.topLevelStack.removeLastOrNull() | ||
| } | ||
| else -> state.currentSubStack.removeLastOrNull() | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 현재 탭의 상세 화면으로 이동한다. | ||
| */ | ||
| private fun goToKey(key: NavKey) { | ||
| state.currentSubStack.apply { | ||
| remove(key) | ||
| add(key) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 다른 top-level 탭으로 이동한다. | ||
| */ | ||
| private fun goToTopLevel(key: NavKey) { | ||
| state.topLevelStack.apply { | ||
| if (key == state.startKey) { | ||
| clear() | ||
| } else { | ||
| remove(key) | ||
| } | ||
| add(key) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 현재 탭을 다시 선택했을 때 root 화면만 남긴다. | ||
| */ | ||
| private fun clearSubStack() { | ||
| state.currentSubStack.run { | ||
| if (size > 1) subList(1, size).clear() | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.