refactor(live): 온에어 접근 가드를 공통화한다

This commit is contained in:
2026-07-14 20:45:29 +09:00
parent 6973816b8c
commit ddc949f9d9
2 changed files with 50 additions and 76 deletions

View File

@@ -8,7 +8,6 @@ import androidx.core.view.isVisible
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.gson.Gson
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerService import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerService
@@ -23,16 +22,10 @@ import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
import kr.co.vividnext.sodalive.live.room.LiveRoomActivity import kr.co.vividnext.sodalive.live.room.LiveRoomActivity
import kr.co.vividnext.sodalive.live.room.dialog.LivePaymentDialog import kr.co.vividnext.sodalive.live.room.dialog.LivePaymentDialog
import kr.co.vividnext.sodalive.live.room.dialog.LiveRoomPasswordDialog import kr.co.vividnext.sodalive.live.room.dialog.LiveRoomPasswordDialog
import kr.co.vividnext.sodalive.mypage.MyPageViewModel
import kr.co.vividnext.sodalive.mypage.auth.Auth
import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
import kr.co.vividnext.sodalive.settings.ContentSettingsActivity
import kr.co.vividnext.sodalive.settings.language.LanguageManager import kr.co.vividnext.sodalive.settings.language.LanguageManager
import kr.co.vividnext.sodalive.settings.language.LocaleHelper import kr.co.vividnext.sodalive.settings.language.LocaleHelper
import kr.co.vividnext.sodalive.splash.SplashActivity import kr.co.vividnext.sodalive.v2.access.AccessRequirement
import kr.co.vividnext.sodalive.user.login.LoginActivity import kr.co.vividnext.sodalive.v2.access.ensureV2Access
import kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog
import kr.co.vividnext.sodalive.v2.live.onair.model.HomeOnAirLivePageUiState import kr.co.vividnext.sodalive.v2.live.onair.model.HomeOnAirLivePageUiState
import kr.co.vividnext.sodalive.v2.live.onair.model.canEnterHomeOnAirLiveRoom import kr.co.vividnext.sodalive.v2.live.onair.model.canEnterHomeOnAirLiveRoom
import kr.co.vividnext.sodalive.v2.live.onair.ui.HomeOnAirLiveAdapter import kr.co.vividnext.sodalive.v2.live.onair.ui.HomeOnAirLiveAdapter
@@ -49,7 +42,6 @@ class HomeOnAirLiveActivity : BaseActivity<ActivityHomeOnAirLiveBinding>(
) { ) {
private val viewModel: HomeOnAirLiveViewModel by viewModel() private val viewModel: HomeOnAirLiveViewModel by viewModel()
private val liveViewModel: LiveViewModel by inject() private val liveViewModel: LiveViewModel by inject()
private val myPageViewModel: MyPageViewModel by inject()
private val loadingDialog: LoadingDialog by lazy { LoadingDialog(this, layoutInflater) } private val loadingDialog: LoadingDialog by lazy { LoadingDialog(this, layoutInflater) }
private val adapter = HomeOnAirLiveAdapter { enterLiveRoom(it.roomId) } private val adapter = HomeOnAirLiveAdapter { enterLiveRoom(it.roomId) }
private var isPageLoading = false private var isPageLoading = false
@@ -118,14 +110,14 @@ class HomeOnAirLiveActivity : BaseActivity<ActivityHomeOnAirLiveBinding>(
} }
private fun enterLiveRoom(roomId: Long) { private fun enterLiveRoom(roomId: Long) {
ensureLoginAndAdultAuth(isAdult = false) { ensureV2Access(AccessRequirement.Login) {
liveViewModel.getRoomDetail(roomId) { roomDetail -> liveViewModel.getRoomDetail(roomId) { roomDetail ->
if (!canEnterHomeOnAirLiveRoom(roomDetail)) { if (!canEnterHomeOnAirLiveRoom(roomDetail)) {
Toast.makeText(applicationContext, R.string.common_error_unknown, Toast.LENGTH_LONG).show() Toast.makeText(applicationContext, R.string.common_error_unknown, Toast.LENGTH_LONG).show()
return@getRoomDetail return@getRoomDetail
} }
ensureLoginAndAdultAuth(isAdult = roomDetail.isAdult) { ensureV2Access(AccessRequirement.forAdultContent(roomDetail.isAdult)) {
enterLiveRoom(roomId, roomDetail) enterLiveRoom(roomId, roomDetail)
} }
} }
@@ -173,70 +165,6 @@ class HomeOnAirLiveActivity : BaseActivity<ActivityHomeOnAirLiveBinding>(
} }
} }
private fun ensureLoginAndAdultAuth(isAdult: Boolean, onAuthed: () -> Unit) {
if (SharedPreferenceManager.token.isBlank()) {
showLoginActivity()
return
}
if (isAdult) {
val isKoreanCountry = SharedPreferenceManager.countryCode.ifBlank { "KR" } == "KR"
if (isKoreanCountry && !SharedPreferenceManager.isAuth) {
V2ModalDialog(
activity = this,
layoutInflater = layoutInflater,
title = getString(R.string.auth_title),
desc = getString(R.string.auth_desc_live),
confirmButtonTitle = getString(R.string.auth_go),
confirmButtonClick = { startAuthFlow() },
cancelButtonTitle = getString(R.string.cancel),
cancelButtonClick = {}
).show(screenWidth)
return
}
if (!SharedPreferenceManager.isAdultContentVisible) {
startActivity(
Intent(applicationContext, ContentSettingsActivity::class.java).apply {
putExtra(Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE, true)
}
)
return
}
}
onAuthed()
}
private fun showLoginActivity() {
if (SharedPreferenceManager.token.isBlank()) {
startActivity(
Intent(applicationContext, LoginActivity::class.java).apply {
putExtra(Constants.EXTRA_DATA, intent.extras)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
)
}
}
private fun startAuthFlow() {
Auth.auth(this, this) { json ->
val bootpayResponse = Gson().fromJson(json, BootpayResponse::class.java)
val request = AuthVerifyRequest(receiptId = bootpayResponse.data.receiptId)
runOnUiThread {
myPageViewModel.authVerify(request) {
startActivity(
Intent(applicationContext, SplashActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
}
)
finish()
}
}
}
}
private fun showPasswordDialog(roomId: Long, can: Int, onEnterRoomSuccess: () -> Unit) { private fun showPasswordDialog(roomId: Long, can: Int, onEnterRoomSuccess: () -> Unit) {
LiveRoomPasswordDialog( LiveRoomPasswordDialog(
activity = this, activity = this,

View File

@@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.v2.live.onair.model.canEnterHomeOnAirLiveRoom
import org.junit.Assert.assertFalse import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import java.io.File
class HomeOnAirLiveEntryPolicyTest { class HomeOnAirLiveEntryPolicyTest {
@@ -25,6 +26,27 @@ class HomeOnAirLiveEntryPolicyTest {
assertFalse(canEnterHomeOnAirLiveRoom(roomDetail(channelName = " "))) assertFalse(canEnterHomeOnAirLiveRoom(roomDetail(channelName = " ")))
} }
@Test
fun `라이브 입장은 로그인 상세 조회 성인 가드 기존 입장 순서를 유지한다`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt"
).readText()
val entrySource = sourceSection(
source = source,
startMarker = "private fun enterLiveRoom(roomId: Long) {",
endMarker = "private fun enterLiveRoom(roomId: Long, roomDetail: GetRoomDetailResponse)"
)
val loginGuard = "ensureV2Access(AccessRequirement.Login)"
val roomDetailQuery = "liveViewModel.getRoomDetail(roomId) { roomDetail ->"
val adultGuard = "ensureV2Access(AccessRequirement.forAdultContent(roomDetail.isAdult))"
val enterRoom = "enterLiveRoom(roomId, roomDetail)"
assertBefore(entrySource, loginGuard, roomDetailQuery)
assertBefore(entrySource, roomDetailQuery, adultGuard)
assertBefore(entrySource, adultGuard, enterRoom)
assertFalse(source.contains("private fun ensureLoginAndAdultAuth"))
}
private fun roomDetail(channelName: String?) = GetRoomDetailResponse( private fun roomDetail(channelName: String?) = GetRoomDetailResponse(
roomId = 1L, roomId = 1L,
price = 0, price = 0,
@@ -55,4 +77,28 @@ class HomeOnAirLiveEntryPolicyTest {
), ),
participatingUsers = emptyList() participatingUsers = emptyList()
) )
private fun sourceSection(source: String, startMarker: String, endMarker: String): String {
val start = source.indexOf(startMarker)
val end = source.indexOf(endMarker, start)
assertTrue("start marker not found: $startMarker", start >= 0)
assertTrue("end marker not found after: $startMarker", end > start)
return source.substring(start, end)
}
private fun assertBefore(source: String, expectedBefore: String, expectedAfter: String) {
val beforeIndex = source.indexOf(expectedBefore)
val afterIndex = source.indexOf(expectedAfter, beforeIndex)
assertTrue("missing source: $expectedBefore", beforeIndex >= 0)
assertTrue("missing source after $expectedBefore: $expectedAfter", afterIndex > beforeIndex)
}
private fun projectFile(relativePath: String): File {
val candidates = listOf(File(relativePath), File("../$relativePath"))
return candidates.firstOrNull { it.exists() }
?: error("Project file not found: $relativePath")
}
} }