fix(live-room): 스크린샷 dead path 제거로 녹화 음소거 정합을 맞춘다
This commit is contained in:
@@ -15,9 +15,6 @@
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<!-- Android 14+ 스크린샷 감지 콜백(registerScreenCaptureCallback)에 필요한 권한 -->
|
||||
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
|
||||
|
||||
<!-- Android 15+ 녹화 상태 콜백(addScreenRecordingCallback)에 필요한 권한 -->
|
||||
<uses-permission android:name="android.permission.DETECT_SCREEN_RECORDING" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
@@ -170,25 +170,15 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
private var isMicrophoneMute = false
|
||||
private var isSpeaker = false
|
||||
|
||||
// 캡처/녹화 감지로 인해 강제 음소거가 필요한지 추적한다.
|
||||
private var isCapturePrivacyMuted = false
|
||||
private var isScreenRecordingActive = false
|
||||
private var isScreenshotMuteActive = false
|
||||
|
||||
// 라이프사이클 중복 호출에서 콜백 재등록을 방지한다.
|
||||
private var isScreenCaptureCallbackRegistered = false
|
||||
private var isScreenRecordingCallbackRegistered = false
|
||||
|
||||
// API 레벨별 콜백 인스턴스를 재사용해 등록/해제 짝을 보장한다.
|
||||
private var screenCaptureCallback: Any? = null
|
||||
private var screenRecordingCallback: Any? = null
|
||||
|
||||
// 스크린샷 이벤트 직후 일정 시간 음소거를 유지한 뒤 자동 해제한다.
|
||||
private val clearScreenshotMuteRunnable = Runnable {
|
||||
isScreenshotMuteActive = false
|
||||
syncCapturePrivacyMuteState()
|
||||
}
|
||||
|
||||
private var isHost = false
|
||||
|
||||
private var isAvailableLikeHeart = false
|
||||
@@ -1597,30 +1587,9 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
}
|
||||
|
||||
private fun registerCaptureSecurityCallbacks() {
|
||||
// 스크린샷/녹화 감지는 지원 API 레벨이 달라 각각 분리 등록한다.
|
||||
registerScreenshotCallback()
|
||||
registerScreenRecordingCallback()
|
||||
}
|
||||
|
||||
private fun registerScreenshotCallback() {
|
||||
// Android 14(API 34)+에서만 스크린샷 감지 콜백을 등록한다.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE || isScreenCaptureCallbackRegistered) {
|
||||
return
|
||||
}
|
||||
|
||||
if (screenCaptureCallback == null) {
|
||||
screenCaptureCallback = ScreenCaptureCallback {
|
||||
onScreenCaptureDetected()
|
||||
}
|
||||
}
|
||||
|
||||
registerScreenCaptureCallback(
|
||||
mainExecutor,
|
||||
screenCaptureCallback as ScreenCaptureCallback
|
||||
)
|
||||
isScreenCaptureCallbackRegistered = true
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun registerScreenRecordingCallback() {
|
||||
// Android 15(API 35)+에서만 스크린녹화 상태 콜백을 등록한다.
|
||||
@@ -1650,24 +1619,10 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
|
||||
private fun unregisterCaptureSecurityCallbacks() {
|
||||
// onStart에서 등록한 콜백을 반대로 해제하고 강제 mute 상태를 정리한다.
|
||||
unregisterScreenshotCallback()
|
||||
unregisterScreenRecordingCallback()
|
||||
clearCapturePrivacyMuteState()
|
||||
}
|
||||
|
||||
private fun unregisterScreenshotCallback() {
|
||||
// Android 14+에서 등록된 스크린샷 콜백만 안전하게 해제한다.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE || !isScreenCaptureCallbackRegistered) {
|
||||
return
|
||||
}
|
||||
|
||||
val callback = screenCaptureCallback as? ScreenCaptureCallback
|
||||
if (callback != null) {
|
||||
unregisterScreenCaptureCallback(callback)
|
||||
}
|
||||
isScreenCaptureCallbackRegistered = false
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun unregisterScreenRecordingCallback() {
|
||||
// Android 15+에서 등록된 녹화 상태 콜백만 안전하게 해제한다.
|
||||
@@ -1682,14 +1637,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
isScreenRecordingCallbackRegistered = false
|
||||
}
|
||||
|
||||
private fun onScreenCaptureDetected() {
|
||||
// 스크린샷 감지 직후 짧은 윈도우 동안 강제 mute를 유지해 유출 여지를 줄인다.
|
||||
isScreenshotMuteActive = true
|
||||
syncCapturePrivacyMuteState()
|
||||
handler.removeCallbacks(clearScreenshotMuteRunnable)
|
||||
handler.postDelayed(clearScreenshotMuteRunnable, SCREEN_CAPTURE_MUTE_HOLD_MILLIS)
|
||||
}
|
||||
|
||||
private fun onScreenRecordingStateChanged(isRecording: Boolean) {
|
||||
// 시스템이 알려준 녹화 가시 상태를 강제 mute 계산에 반영한다.
|
||||
isScreenRecordingActive = isRecording
|
||||
@@ -1698,15 +1645,12 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
|
||||
private fun clearCapturePrivacyMuteState() {
|
||||
// 라이프사이클 해제 시 캡처 기반 플래그를 모두 초기화해 원복을 보장한다.
|
||||
handler.removeCallbacks(clearScreenshotMuteRunnable)
|
||||
isScreenRecordingActive = false
|
||||
isScreenshotMuteActive = false
|
||||
syncCapturePrivacyMuteState()
|
||||
}
|
||||
|
||||
private fun syncCapturePrivacyMuteState() {
|
||||
// 스크린샷/녹화 감지를 하나의 강제 mute 상태로 통합한다.
|
||||
val shouldMute = isScreenRecordingActive || isScreenshotMuteActive
|
||||
val shouldMute = isScreenRecordingActive
|
||||
if (isCapturePrivacyMuted == shouldMute) {
|
||||
return
|
||||
}
|
||||
@@ -4230,7 +4174,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
|
||||
companion object {
|
||||
private const val NO_CHATTING_TIME = 180L
|
||||
private const val SCREEN_CAPTURE_MUTE_HOLD_MILLIS = 2_000L
|
||||
var isForeground: Boolean = false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user