diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt index 987c92c6..7efffcf0 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt @@ -190,6 +190,10 @@ class LiveRoomActivity : BaseActivity(ActivityLiveRoomB // joinChannel 중복 호출 방지 플래그 private var hasInvokedJoinChannel = false + // RTM/RTC 연결 완료 추적 플래그 (둘 다 연결되면 레이아웃 강제 갱신) + private var isRtcJoined = false + private var isRtmJoined = false + private var v2vSourceLanguage: String? = null private var v2vTargetLanguage: String? = null private var isV2vAvailable = false @@ -731,6 +735,41 @@ class LiveRoomActivity : BaseActivity(ActivityLiveRoomB } // endregion + // RTM과 RTC가 모두 연결되면 키보드를 잠깐 올렸다 내려 레이아웃을 강제 갱신한다. + // 로딩 다이얼로그가 화면을 덮고 있는 동안 수행하여 사용자에게 변화가 보이지 않도록 한다. + private fun tryForceLayoutRefresh(): Boolean { + if (!isRtcJoined || !isRtmJoined) return false + + handler.post { + // 키보드가 화면을 밀어올리지 않도록 임시로 adjustNothing 전환 + window.setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN + or WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING + ) + + binding.etChat.requestFocus() + imm.showSoftInput(binding.etChat, InputMethodManager.SHOW_IMPLICIT) + + handler.postDelayed({ + imm.hideSoftInputFromWindow( + binding.etChat.windowToken, + InputMethodManager.HIDE_NOT_ALWAYS + ) + binding.etChat.clearFocus() + + // 원래 softInputMode 복원 + window.setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN + or WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN + ) + + // 키보드 트릭 완료 후 로딩 다이얼로그 dismiss + loadingDialog.dismiss() + }, 200) + } + return true + } + private fun applyKeyboardPanInsets() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { return @@ -2188,6 +2227,8 @@ class LiveRoomActivity : BaseActivity(ActivityLiveRoomB override fun onJoinChannelSuccess(channel: String, uid: Int, elapsed: Int) { super.onJoinChannelSuccess(channel, uid, elapsed) Logger.e("onJoinChannelSuccess - uid: $uid, channel: $channel") + isRtcJoined = true + tryForceLayoutRefresh() } override fun onStreamMessage(uid: Int, streamId: Int, data: ByteArray?) { @@ -2834,8 +2875,10 @@ class LiveRoomActivity : BaseActivity(ActivityLiveRoomB rtmToken = roomInfo.rtmToken, channelName = roomInfo.channelName, rtmChannelJoinSuccess = { - handler.post { - loadingDialog.dismiss() + isRtmJoined = true + // 두 채널 모두 연결 시 키보드 트릭 후 dismiss, 아니면 즉시 dismiss + if (!tryForceLayoutRefresh()) { + handler.post { loadingDialog.dismiss() } } if (userId == roomInfo.creatorId) { diff --git a/docs/20260329_라이브룸_UI미갱신_버그수정.md b/docs/20260329_라이브룸_UI미갱신_버그수정.md new file mode 100644 index 00000000..4b0b0f60 --- /dev/null +++ b/docs/20260329_라이브룸_UI미갱신_버그수정.md @@ -0,0 +1,57 @@ +# 라이브룸 UI 미갱신 버그 수정 + +## 현상 +- 라이브 입장 후 공지, 메뉴판 터치 시 UI가 보이지 않음 +- 상대방 채팅이 화면에 갱신되지 않음 +- 방장이 아닌 유저에게서 두드러지게 나타남 +- 키보드가 올라오거나 화면에 변화가 생기면 모든 것이 해결됨 + +## 원인 분석 +- `BaseActivity`에서 `WindowCompat.setDecorFitsSystemWindows(window, false)` (edge-to-edge) 적용 +- `LiveRoomActivity`의 manifest에 `adjustPan` 설정과 edge-to-edge가 충돌 +- `adjustPan`은 시스템이 창을 pan 하려 하지만, edge-to-edge 모드에서는 앱이 insets을 직접 처리 +- 이 충돌로 DecorView 내부 스크롤 트래킹 상태가 불일치하여 `invalidate()` 더티 영역 계산 오류 발생 +- 키보드가 올라가면 시스템이 WindowInsets를 재분배하고, `OnApplyWindowInsetsListener`에서 `setPadding()` 호출 → `requestLayout()` → 전체 레이아웃 패스가 강제 수행되어 해결됨 + +## 수정 방법 +- RTM과 RTC가 모두 연결 완료된 시점에 키보드를 프로그래밍적으로 올렸다 내려 레이아웃을 강제 갱신 +- `isRtcJoined`, `isRtmJoined` 플래그로 두 연결 상태를 추적 +- 두 플래그가 모두 true가 되면 `tryForceLayoutRefresh()`를 호출 + +### 눈속임 처리 (사용자에게 변화가 보이지 않도록) +1. 로딩 다이얼로그가 화면을 덮고 있는 동안 키보드 트릭을 수행 +2. `adjustNothing`으로 임시 전환하여 키보드가 화면을 밀어올리지 않도록 방지 +3. 키보드 show → 200ms 후 hide → `adjustPan` 복원 → 로딩 다이얼로그 dismiss +4. RTM 콜백의 `loadingDialog.dismiss()`를 `tryForceLayoutRefresh()` 내부로 이동 + +## 수정 계획 + +- [x] `isRtcJoined`, `isRtmJoined` 플래그 추가 +- [x] `onJoinChannelSuccess`에서 `isRtcJoined = true` 설정 및 `tryForceLayoutRefresh()` 호출 +- [x] RTM 성공 콜백에서 `isRtmJoined = true` 설정 및 `tryForceLayoutRefresh()` 호출 +- [x] `tryForceLayoutRefresh()` 메서드 구현 + - [x] adjustNothing 임시 전환으로 화면 이동 방지 + - [x] 로딩 다이얼로그 뒤에서 키보드 트릭 수행 + - [x] 완료 후 adjustPan 복원 및 로딩 다이얼로그 dismiss + - [x] Boolean 반환으로 RTM 콜백에서 fallback dismiss 처리 +- [x] 빌드 검증 + +## 검증 기록 + +### 2026-03-29 빌드 검증 (1차 - adjustNothing 방식) +- 명령: `./gradlew :app:assembleDebug` +- 결과: BUILD SUCCESSFUL (16s, 46 tasks) +- 변경 파일: `AndroidManifest.xml` (adjustPan→adjustNothing), `LiveRoomActivity.kt` (API S→R) +- 비고: 실기기에서 효과 없음 → 되돌림 + +### 2026-03-29 빌드 검증 (2차 - 키보드 강제 갱신 방식) +- 명령: `./gradlew :app:assembleDebug` +- 결과: BUILD SUCCESSFUL (21s, 46 tasks) +- 변경 파일: `LiveRoomActivity.kt` (RTM/RTC 연결 완료 후 키보드 올렸다 내리기) +- 비고: 키보드가 화면을 위로 밀어올리는 것이 사용자에게 보임 → 눈속임 개선 필요 + +### 2026-03-29 빌드 검증 (3차 - 눈속임 개선) +- 명령: `./gradlew :app:assembleDebug` +- 결과: BUILD SUCCESSFUL (18s, 46 tasks) +- 변경 파일: `LiveRoomActivity.kt` +- 방식: adjustNothing 임시 전환 + 로딩 다이얼로그 뒤에서 키보드 트릭 수행