From 1e0a6d20fbfed4d0f6ffd74737d94a8423f7a01f Mon Sep 17 00:00:00 2001 From: soyeonLee <109227292+soyeonLee126@users.noreply.github.com> Date: Tue, 18 Aug 2026 14:36:40 +0900 Subject: [PATCH 1/6] =?UTF-8?q?fix:=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=EC=A0=9C=EC=8A=A4=EC=B2=98=20=EB=92=A4=EB=A1=9C=EA=B0=80?= =?UTF-8?q?=EA=B8=B0=20=ED=8A=B8=EB=9E=9C=EC=A7=80=EC=85=98=20=EB=B3=B4?= =?UTF-8?q?=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 탭 화면에 predictivePopTransitionSpec 을 지정해, 제스처 뒤로가기만 라이브러리 기본값(scaleOut)으로 축소되던 모션을 fade-through 로 통일 - 상세 화면 pop 이 swipeEdge 를 반영해 제스처를 시작한 엣지 쪽으로 빠지도록 수정 - swipeEdge 상수 사용을 위해 navigationevent 의존성을 명시 선언 - detailSlideTransition 주석의 트리거 경로 설명 정정 --- app/build.gradle.kts | 1 + .../com/gamss/android/app/main/MainScreen.kt | 19 ++++++++++++------- gradle/libs.versions.toml | 2 ++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ef5b6ece..f022c137 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -118,6 +118,7 @@ dependencies { implementation(libs.navigation3.runtime) implementation(libs.navigation3.ui) + implementation(libs.androidx.navigationevent) implementation(libs.orbit.core) implementation(libs.orbit.viewmodel) diff --git a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt index 8063a072..a39097c1 100644 --- a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt +++ b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt @@ -25,6 +25,7 @@ import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.entryProvider import androidx.navigation3.scene.Scene import androidx.navigation3.ui.NavDisplay +import androidx.navigationevent.NavigationEvent import com.gamss.android.app.navigation.Navigator import com.gamss.android.app.navigation.bottomBarItems import com.gamss.android.app.navigation.keys @@ -95,8 +96,10 @@ fun MainScreen( // 최상위 탭(Home/Calendar/Chat) 전환 기본값. 탭은 위계 없는 형제 화면이라 // 방향성 있는 슬라이드 대신 fade-through를 쓴다. 상세 화면은 아래 entry의 // metadata(detailTransition)가 이 기본값을 덮어쓴다. + // 탭 entry에는 metadata가 없어 세 spec을 모두 지정한다. 빠뜨리면 그 경로만 scaleOut이 된다. transitionSpec = tabFadeThroughSpec, popTransitionSpec = tabFadeThroughSpec, + predictivePopTransitionSpec = { tabFadeThroughSpec(this) }, onBack = { if (navigationState.canGoBack) { navigator.goBack() @@ -175,10 +178,10 @@ private val tabFadeThroughSpec: AnimatedContentTransitionScope>.() * 탭 내부의 상세 화면(Setting/AccountInfo/NicknameChange/ChatRoom/WebView) push·pop 전용 * 트랜지션. 계층 이동이라는 방향감을 주기 위해 좌우 슬라이드(shared axis X)를 쓴다. * - * popTransitionSpec과 predictivePopTransitionSpec을 반드시 같이 지정해야 한다 — 인앱 뒤로가기 - * 아이콘(Navigator.goBack() 직접 호출)은 popTransitionSpec을, 폰의 시스템 뒤로가기(제스처·버튼 - * 모두 OnBackInvokedCallback 경유)는 predictivePopTransitionSpec을 따로 참조하기 때문에, 하나만 - * 지정하면 트리거 경로에 따라 모션이 달라져 버린다. + * popTransitionSpec과 predictivePopTransitionSpec을 반드시 같이 지정해야 한다. 진행 중인 제스처 + * 뒤로가기만 predictivePopTransitionSpec을 타고, 인앱 뒤로가기 아이콘(Navigator.goBack() 직접 + * 호출)과 3버튼 뒤로가기는 popTransitionSpec을 타기 때문에, 하나만 지정하면 트리거 경로에 따라 + * 모션이 달라져 버린다. */ private val detailSlideTransition: Map = NavDisplay.transitionSpec { (slideIntoContainer(SlideDirection.Start, tween(300)) + fadeIn(tween(300))) togetherWith @@ -186,9 +189,11 @@ private val detailSlideTransition: Map = NavDisplay.transitionSpec } + NavDisplay.popTransitionSpec { (slideIntoContainer(SlideDirection.End, tween(300)) + fadeIn(tween(300))) togetherWith (slideOutOfContainer(SlideDirection.End, tween(300)) + fadeOut(tween(150))) -} + NavDisplay.predictivePopTransitionSpec { _ -> - (slideIntoContainer(SlideDirection.End, tween(300)) + fadeIn(tween(300))) togetherWith - (slideOutOfContainer(SlideDirection.End, tween(300)) + fadeOut(tween(150))) +} + NavDisplay.predictivePopTransitionSpec { swipeEdge -> + // 제스처를 시작한 엣지 쪽으로 화면이 빠져야 손가락 방향과 모션이 어긋나지 않는다. + val towards = if (swipeEdge == NavigationEvent.EDGE_RIGHT) SlideDirection.Start else SlideDirection.End + (slideIntoContainer(towards, tween(300)) + fadeIn(tween(300))) togetherWith + (slideOutOfContainer(towards, tween(300)) + fadeOut(tween(150))) } /** diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e3b3c180..b67d0175 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,6 +12,7 @@ androidx-lifecycle = "2.9.4" lifecycle-viewmodel-navigation3 = "2.10.0" compose-bom = "2026.04.01" navigation3 = "1.1.0-rc01" +navigationevent = "1.0.2" junit = "4.13.2" detekt = "1.23.8" kotlinx-serialization = "1.11.0" @@ -60,6 +61,7 @@ compose-material-icons-core = { module = "androidx.compose.material:material-ico navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "navigation3" } navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "navigation3" } +androidx-navigationevent = { module = "androidx.navigationevent:navigationevent", version.ref = "navigationevent" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" } From 04c1e996b7d7e778364c7ce4a7e5096960ab9cdb Mon Sep 17 00:00:00 2001 From: soyeonLee <109227292+soyeonLee126@users.noreply.github.com> Date: Tue, 18 Aug 2026 14:37:03 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=ED=99=88=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=92=A4=EB=A1=9C=EA=B0=80=EA=B8=B0=20=EB=91=90=20=EB=B2=88=20?= =?UTF-8?q?=EC=97=B0=ED=83=80=20=EC=8B=9C=20=EC=95=B1=20=EC=A2=85=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 홈 루트에서 뒤로가기 한 번이면 안내 토스트, 2초 안에 한 번 더 누르면 종료 - 토스트 인스턴스를 재사용하고 종료 직전 취소해, 연타 시 큐에 쌓이거나 앱이 사라진 뒤 런처 위에 남지 않도록 처리 - 다른 화면을 거쳐 홈으로 돌아오면 직전 경고를 무효화 --- .../com/gamss/android/app/main/MainScreen.kt | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt index a39097c1..c95ba34a 100644 --- a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt +++ b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt @@ -1,5 +1,8 @@ package com.gamss.android.app.main +import android.os.SystemClock +import android.widget.Toast +import androidx.activity.compose.BackHandler import androidx.activity.compose.LocalActivity import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.AnimatedContentTransitionScope.SlideDirection @@ -17,9 +20,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.entryProvider @@ -72,6 +78,7 @@ fun MainScreen( val snackbarHostState = remember { SnackbarHostState() } ModelDownloadConfirmationEffect(modelDownloadPromptViewModel, snackbarHostState) + DoubleBackToExitHandler(enabled = { !navigationState.canGoBack }) // 배경/탭바 에셋이 라이트 전용이라 다크 시안이 나올 때까지 셸은 라이트로 고정한다. GamssTheme(darkTheme = false) { @@ -196,6 +203,39 @@ private val detailSlideTransition: Map = NavDisplay.transitionSpec (slideOutOfContainer(towards, tween(300)) + fadeOut(tween(150))) } +/** + * 홈 루트에서만 뒤로가기를 가로채 두 번 연타로 앱을 종료한다. 홈 루트에서는 NavDisplay의 + * previousEntries가 비어 자체 back handler가 꺼지므로, 이 handler가 시스템 뒤로가기를 대신 받는다. + */ +@Composable +private fun DoubleBackToExitHandler(enabled: () -> Boolean) { + val activity = LocalActivity.current + val context = LocalContext.current + // 같은 인스턴스를 재사용해야 연타 시 토스트가 큐에 쌓이지 않고, 종료 직전 취소할 수 있다. + val toast = remember(context) { Toast.makeText(context, EXIT_CONFIRM_MESSAGE, Toast.LENGTH_SHORT) } + var lastBackPressedAt by remember { mutableLongStateOf(NO_BACK_PRESS) } + val isEnabled = enabled() + + // 다른 화면을 거쳐 홈으로 돌아오면 직전 경고는 무효다. 남겨두면 경고 없이 바로 종료된다. + LaunchedEffect(isEnabled) { + if (!isEnabled) lastBackPressedAt = NO_BACK_PRESS + } + + BackHandler(enabled = isEnabled) { + // 벽시계(currentTimeMillis)는 시간 보정으로 뒤로 점프해 창 계산을 깨뜨린다. + val now = SystemClock.elapsedRealtime() + val withinWindow = lastBackPressedAt != NO_BACK_PRESS && now - lastBackPressedAt <= EXIT_CONFIRM_WINDOW_MS + if (withinWindow) { + // 취소하지 않으면 앱이 사라진 뒤에도 런처 위에 토스트가 남는다. + toast.cancel() + activity?.finish() + } else { + lastBackPressedAt = now + toast.show() + } + } +} + /** * emotion/summary 온디바이스 모델 중 하나라도 셀룰러/크기 확인이 필요해지면 스낵바를 띄운다. * 상태 기반이라 한 세션에서 여러 번(예: emotion 이 먼저, summary 가 나중에) 뜰 수 있다 — @@ -223,5 +263,9 @@ private fun ModelDownloadConfirmationEffect( } } +private const val NO_BACK_PRESS = 0L +private const val EXIT_CONFIRM_WINDOW_MS = 2000L +private const val EXIT_CONFIRM_MESSAGE = "뒤로가기를 한 번 더 누르면 종료돼요" + private const val MODEL_DOWNLOAD_MESSAGE = "추가 다운로드가 필요해요" private const val MODEL_DOWNLOAD_ACTION = "모바일 데이터로 받기" From 330c0b92593919241388526f6d3e6ed052695e05 Mon Sep 17 00:00:00 2001 From: soyeonLee <109227292+soyeonLee126@users.noreply.github.com> Date: Tue, 18 Aug 2026 16:37:17 +0900 Subject: [PATCH 3/6] =?UTF-8?q?refactor:=20=EC=A2=85=EB=A3=8C=20=EC=95=88?= =?UTF-8?q?=EB=82=B4=20=EB=AC=B8=EA=B5=AC=EB=A5=BC=20=EB=AC=B8=EC=9E=90?= =?UTF-8?q?=EC=97=B4=20=EB=A6=AC=EC=86=8C=EC=8A=A4=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EA=B3=A0=20=EC=84=A4=EB=AA=85=ED=98=95=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 하드코딩한 종료 안내 문구를 strings.xml 의 back_press_exit_confirm 으로 이동 - 코드에서 읽히는 동작을 되풀이하는 주석을 걷어내고, 코드만으로 알 수 없는 제약 두 건(경고 무효화 이유, 토스트 취소 이유)만 남김 --- .../com/gamss/android/app/main/MainScreen.kt | 24 ++++++++----------- app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt index c95ba34a..5d803e0a 100644 --- a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt +++ b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt @@ -32,6 +32,7 @@ import androidx.navigation3.runtime.entryProvider import androidx.navigation3.scene.Scene import androidx.navigation3.ui.NavDisplay import androidx.navigationevent.NavigationEvent +import com.gamss.android.app.R import com.gamss.android.app.navigation.Navigator import com.gamss.android.app.navigation.bottomBarItems import com.gamss.android.app.navigation.keys @@ -103,7 +104,6 @@ fun MainScreen( // 최상위 탭(Home/Calendar/Chat) 전환 기본값. 탭은 위계 없는 형제 화면이라 // 방향성 있는 슬라이드 대신 fade-through를 쓴다. 상세 화면은 아래 entry의 // metadata(detailTransition)가 이 기본값을 덮어쓴다. - // 탭 entry에는 metadata가 없어 세 spec을 모두 지정한다. 빠뜨리면 그 경로만 scaleOut이 된다. transitionSpec = tabFadeThroughSpec, popTransitionSpec = tabFadeThroughSpec, predictivePopTransitionSpec = { tabFadeThroughSpec(this) }, @@ -185,10 +185,8 @@ private val tabFadeThroughSpec: AnimatedContentTransitionScope>.() * 탭 내부의 상세 화면(Setting/AccountInfo/NicknameChange/ChatRoom/WebView) push·pop 전용 * 트랜지션. 계층 이동이라는 방향감을 주기 위해 좌우 슬라이드(shared axis X)를 쓴다. * - * popTransitionSpec과 predictivePopTransitionSpec을 반드시 같이 지정해야 한다. 진행 중인 제스처 - * 뒤로가기만 predictivePopTransitionSpec을 타고, 인앱 뒤로가기 아이콘(Navigator.goBack() 직접 - * 호출)과 3버튼 뒤로가기는 popTransitionSpec을 타기 때문에, 하나만 지정하면 트리거 경로에 따라 - * 모션이 달라져 버린다. + * 진행 중인 제스처 뒤로가기만 predictivePopTransitionSpec을 타고 나머지 트리거는 + * popTransitionSpec을 타므로, 셋 다 지정하지 않으면 트리거 경로에 따라 모션이 갈린다. */ private val detailSlideTransition: Map = NavDisplay.transitionSpec { (slideIntoContainer(SlideDirection.Start, tween(300)) + fadeIn(tween(300))) togetherWith @@ -197,36 +195,35 @@ private val detailSlideTransition: Map = NavDisplay.transitionSpec (slideIntoContainer(SlideDirection.End, tween(300)) + fadeIn(tween(300))) togetherWith (slideOutOfContainer(SlideDirection.End, tween(300)) + fadeOut(tween(150))) } + NavDisplay.predictivePopTransitionSpec { swipeEdge -> - // 제스처를 시작한 엣지 쪽으로 화면이 빠져야 손가락 방향과 모션이 어긋나지 않는다. + // 제스처를 시작한 엣지 쪽으로 빠져야 손가락 방향과 맞는다. val towards = if (swipeEdge == NavigationEvent.EDGE_RIGHT) SlideDirection.Start else SlideDirection.End (slideIntoContainer(towards, tween(300)) + fadeIn(tween(300))) togetherWith (slideOutOfContainer(towards, tween(300)) + fadeOut(tween(150))) } /** - * 홈 루트에서만 뒤로가기를 가로채 두 번 연타로 앱을 종료한다. 홈 루트에서는 NavDisplay의 - * previousEntries가 비어 자체 back handler가 꺼지므로, 이 handler가 시스템 뒤로가기를 대신 받는다. + * 홈 루트에서는 NavDisplay의 previousEntries가 비어 자체 back handler가 꺼지므로 이 handler가 받는다. */ @Composable private fun DoubleBackToExitHandler(enabled: () -> Boolean) { val activity = LocalActivity.current val context = LocalContext.current - // 같은 인스턴스를 재사용해야 연타 시 토스트가 큐에 쌓이지 않고, 종료 직전 취소할 수 있다. - val toast = remember(context) { Toast.makeText(context, EXIT_CONFIRM_MESSAGE, Toast.LENGTH_SHORT) } + val toast = remember(context) { + Toast.makeText(context, R.string.back_press_exit_confirm, Toast.LENGTH_SHORT) + } var lastBackPressedAt by remember { mutableLongStateOf(NO_BACK_PRESS) } val isEnabled = enabled() - // 다른 화면을 거쳐 홈으로 돌아오면 직전 경고는 무효다. 남겨두면 경고 없이 바로 종료된다. + // 다른 화면을 거쳐 돌아오면 직전 경고는 무효다. 남겨두면 경고 없이 종료된다. LaunchedEffect(isEnabled) { if (!isEnabled) lastBackPressedAt = NO_BACK_PRESS } BackHandler(enabled = isEnabled) { - // 벽시계(currentTimeMillis)는 시간 보정으로 뒤로 점프해 창 계산을 깨뜨린다. val now = SystemClock.elapsedRealtime() val withinWindow = lastBackPressedAt != NO_BACK_PRESS && now - lastBackPressedAt <= EXIT_CONFIRM_WINDOW_MS if (withinWindow) { - // 취소하지 않으면 앱이 사라진 뒤에도 런처 위에 토스트가 남는다. + // 취소하지 않으면 앱이 사라진 뒤에도 런처 위에 남는다. toast.cancel() activity?.finish() } else { @@ -265,7 +262,6 @@ private fun ModelDownloadConfirmationEffect( private const val NO_BACK_PRESS = 0L private const val EXIT_CONFIRM_WINDOW_MS = 2000L -private const val EXIT_CONFIRM_MESSAGE = "뒤로가기를 한 번 더 누르면 종료돼요" private const val MODEL_DOWNLOAD_MESSAGE = "추가 다운로드가 필요해요" private const val MODEL_DOWNLOAD_ACTION = "모바일 데이터로 받기" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 774ac6e5..ef5c6076 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,4 +5,6 @@ 보관함 대화 + + 뒤로가기를 한 번 더 누르면 종료돼요 From c35ae0474e4bfbcfbe5cfeca85a5ecbfada7fb20 Mon Sep 17 00:00:00 2001 From: soyeonLee <109227292+soyeonLee126@users.noreply.github.com> Date: Tue, 18 Aug 2026 16:43:29 +0900 Subject: [PATCH 4/6] =?UTF-8?q?refactor:=20=EB=AA=A8=EB=8D=B8=20=EB=8B=A4?= =?UTF-8?q?=EC=9A=B4=EB=A1=9C=EB=93=9C=20=EC=8A=A4=EB=82=B5=EB=B0=94=20?= =?UTF-8?q?=EB=AC=B8=EA=B5=AC=EB=A5=BC=20=EB=AC=B8=EC=9E=90=EC=97=B4=20?= =?UTF-8?q?=EB=A6=AC=EC=86=8C=EC=8A=A4=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MODEL_DOWNLOAD_MESSAGE / MODEL_DOWNLOAD_ACTION 상수를 strings.xml 로 이동 - LaunchedEffect 안에서는 stringResource 를 부를 수 없어 composable 본문에서 미리 읽는다 --- .../main/kotlin/com/gamss/android/app/main/MainScreen.kt | 9 +++++---- app/src/main/res/values/strings.xml | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt index 5d803e0a..01a8afcf 100644 --- a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt +++ b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt @@ -26,6 +26,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.entryProvider @@ -245,13 +246,15 @@ private fun ModelDownloadConfirmationEffect( ) { val activity = LocalActivity.current val needsConfirmation by viewModel.needsUserConfirmation.collectAsState() + val message = stringResource(R.string.model_download_message) + val actionLabel = stringResource(R.string.model_download_action) LaunchedEffect(needsConfirmation, activity) { if (!needsConfirmation || activity == null) return@LaunchedEffect val result = snackbarHostState.showSnackbar( - message = MODEL_DOWNLOAD_MESSAGE, - actionLabel = MODEL_DOWNLOAD_ACTION, + message = message, + actionLabel = actionLabel, withDismissAction = true, ) if (result == SnackbarResult.ActionPerformed) { @@ -263,5 +266,3 @@ private fun ModelDownloadConfirmationEffect( private const val NO_BACK_PRESS = 0L private const val EXIT_CONFIRM_WINDOW_MS = 2000L -private const val MODEL_DOWNLOAD_MESSAGE = "추가 다운로드가 필요해요" -private const val MODEL_DOWNLOAD_ACTION = "모바일 데이터로 받기" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ef5c6076..41b4254b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,4 +7,7 @@ 대화 뒤로가기를 한 번 더 누르면 종료돼요 + + 추가 다운로드가 필요해요 + 모바일 데이터로 받기 From 18a7cdc70cc3e89f828c9428a977ab66895b58ea Mon Sep 17 00:00:00 2001 From: soyeonLee <109227292+soyeonLee126@users.noreply.github.com> Date: Wed, 19 Aug 2026 10:46:24 +0900 Subject: [PATCH 5/6] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=BD=ED=9E=88=EB=8A=94=20=EC=A3=BC=EC=84=9D=EC=9D=84=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC=ED=95=A9=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 슬라이드 방향 분기를 그대로 되풀이하던 주석을 지웁니다 - 남긴 주석은 라이브러리 동작과 재발 방지 정보만 담도록 다듬습니다 --- .../kotlin/com/gamss/android/app/main/MainScreen.kt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt index 15ff8dd2..532a0f48 100644 --- a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt +++ b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt @@ -205,8 +205,7 @@ private val tabFadeThroughSpec: AnimatedContentTransitionScope>.() * 탭 내부의 상세 화면(Setting/AccountInfo/NicknameChange/ChatRoom/WebView) push·pop 전용 * 트랜지션. 계층 이동이라는 방향감을 주기 위해 좌우 슬라이드(shared axis X)를 쓴다. * - * 진행 중인 제스처 뒤로가기만 predictivePopTransitionSpec을 타고 나머지 트리거는 - * popTransitionSpec을 타므로, 셋 다 지정하지 않으면 트리거 경로에 따라 모션이 갈린다. + * 트리거 경로마다 참조하는 spec이 달라서, 셋 다 지정하지 않으면 모션이 갈립니다. */ private val detailSlideTransition: Map = NavDisplay.transitionSpec { (slideIntoContainer(SlideDirection.Start, tween(300)) + fadeIn(tween(300))) togetherWith @@ -215,14 +214,13 @@ private val detailSlideTransition: Map = NavDisplay.transitionSpec (slideIntoContainer(SlideDirection.End, tween(300)) + fadeIn(tween(300))) togetherWith (slideOutOfContainer(SlideDirection.End, tween(300)) + fadeOut(tween(150))) } + NavDisplay.predictivePopTransitionSpec { swipeEdge -> - // 제스처를 시작한 엣지 쪽으로 빠져야 손가락 방향과 맞는다. val towards = if (swipeEdge == NavigationEvent.EDGE_RIGHT) SlideDirection.Start else SlideDirection.End (slideIntoContainer(towards, tween(300)) + fadeIn(tween(300))) togetherWith (slideOutOfContainer(towards, tween(300)) + fadeOut(tween(150))) } /** - * 홈 루트에서는 NavDisplay의 previousEntries가 비어 자체 back handler가 꺼지므로 이 handler가 받는다. + * 홈 루트에서는 NavDisplay의 previousEntries가 비어 자체 back handler가 꺼지므로 이 handler가 받습니다. */ @Composable private fun DoubleBackToExitHandler(enabled: () -> Boolean) { @@ -234,7 +232,7 @@ private fun DoubleBackToExitHandler(enabled: () -> Boolean) { var lastBackPressedAt by remember { mutableLongStateOf(NO_BACK_PRESS) } val isEnabled = enabled() - // 다른 화면을 거쳐 돌아오면 직전 경고는 무효다. 남겨두면 경고 없이 종료된다. + // 다른 화면을 거쳐 돌아오면 직전 경고는 무효입니다. 남겨두면 경고 없이 종료됩니다. LaunchedEffect(isEnabled) { if (!isEnabled) lastBackPressedAt = NO_BACK_PRESS } @@ -243,7 +241,7 @@ private fun DoubleBackToExitHandler(enabled: () -> Boolean) { val now = SystemClock.elapsedRealtime() val withinWindow = lastBackPressedAt != NO_BACK_PRESS && now - lastBackPressedAt <= EXIT_CONFIRM_WINDOW_MS if (withinWindow) { - // 취소하지 않으면 앱이 사라진 뒤에도 런처 위에 남는다. + // 취소하지 않으면 앱이 사라진 뒤에도 런처 위에 남습니다. toast.cancel() activity?.finish() } else { From 1cfd8f2ed00bb4a509014ba890da2de4a3763c0b Mon Sep 17 00:00:00 2001 From: soyeonLee <109227292+soyeonLee126@users.noreply.github.com> Date: Wed, 19 Aug 2026 11:23:48 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=ED=8C=8C=EC=9D=BC=20=EB=81=9D?= =?UTF-8?q?=EC=97=90=20=EB=82=A8=EC=9D=80=20=EB=B9=88=20=EC=A4=84=EC=9D=84?= =?UTF-8?q?=20=EC=A7=80=EC=9B=81=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 문자열 상수를 리소스로 옮길 때 빈 줄이 남아 detekt 의 NoConsecutiveBlankLines 규칙에 걸렸습니다. --- app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt index 532a0f48..a665b2ad 100644 --- a/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt +++ b/app/src/main/kotlin/com/gamss/android/app/main/MainScreen.kt @@ -282,4 +282,3 @@ private fun ModelDownloadConfirmationEffect( private const val NO_BACK_PRESS = 0L private const val EXIT_CONFIRM_WINDOW_MS = 2000L -