feat(access): 공통 접근 실행기를 추가한다
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
package kr.co.vividnext.sodalive.v2.access
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.gson.Gson
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
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.splash.SplashActivity
|
||||
import kr.co.vividnext.sodalive.user.login.LoginActivity
|
||||
import kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog
|
||||
import org.koin.android.ext.android.get
|
||||
|
||||
class V2AccessGuard(
|
||||
private val policy: AccessPolicy = AccessPolicy(),
|
||||
private val stateProvider: AccessStateProvider = SharedPreferenceAccessStateProvider()
|
||||
) {
|
||||
fun ensureAccess(
|
||||
activity: Activity,
|
||||
requirement: AccessRequirement,
|
||||
onAllowed: () -> Unit = {}
|
||||
): Boolean {
|
||||
return handleDecision(
|
||||
activity = activity,
|
||||
requirement = requirement,
|
||||
startLogin = { startLoginActivity(activity) },
|
||||
onAllowed = onAllowed
|
||||
)
|
||||
}
|
||||
|
||||
fun ensureAccess(
|
||||
fragment: Fragment,
|
||||
requirement: AccessRequirement,
|
||||
onAllowed: () -> Unit = {}
|
||||
): Boolean {
|
||||
val activity = fragment.requireActivity()
|
||||
return handleDecision(
|
||||
activity = activity,
|
||||
requirement = requirement,
|
||||
startLogin = { startLoginActivity(activity) },
|
||||
onAllowed = onAllowed
|
||||
)
|
||||
}
|
||||
|
||||
fun isAllowed(requirement: AccessRequirement): Boolean {
|
||||
return policy.decide(requirement, stateProvider.current()) == AccessDecision.Allowed
|
||||
}
|
||||
|
||||
private fun handleDecision(
|
||||
activity: Activity,
|
||||
requirement: AccessRequirement,
|
||||
startLogin: () -> Unit,
|
||||
onAllowed: () -> Unit
|
||||
): Boolean {
|
||||
return executeAccessDecision(
|
||||
decision = policy.decide(requirement, stateProvider.current()),
|
||||
onAllowed = onAllowed,
|
||||
onLoginRequired = startLogin,
|
||||
onAdultVerificationRequired = { showAuthDialog(activity) },
|
||||
onAdultContentSettingRequired = { startContentSettings(activity) }
|
||||
)
|
||||
}
|
||||
|
||||
private fun startLoginActivity(activity: Activity) {
|
||||
activity.startActivity(
|
||||
Intent(activity, LoginActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_DATA, activity.intent.extras)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun showAuthDialog(activity: Activity) {
|
||||
V2ModalDialog(
|
||||
activity = activity,
|
||||
layoutInflater = activity.layoutInflater,
|
||||
title = activity.getString(R.string.auth_title),
|
||||
desc = activity.getString(R.string.auth_desc_live),
|
||||
confirmButtonTitle = activity.getString(R.string.auth_go),
|
||||
confirmButtonClick = { startAuthFlow(activity) },
|
||||
cancelButtonTitle = activity.getString(R.string.cancel),
|
||||
cancelButtonClick = {}
|
||||
).show(activity.resources.displayMetrics.widthPixels)
|
||||
}
|
||||
|
||||
private fun startAuthFlow(activity: Activity) {
|
||||
val myPageViewModel: MyPageViewModel = activity.get()
|
||||
Auth.auth(activity, activity) { json ->
|
||||
val bootpayResponse = Gson().fromJson(json, BootpayResponse::class.java)
|
||||
val request = AuthVerifyRequest(receiptId = bootpayResponse.data.receiptId)
|
||||
activity.runOnUiThread {
|
||||
if (activity.isFinishing || activity.isDestroyed) return@runOnUiThread
|
||||
myPageViewModel.authVerify(request) {
|
||||
if (activity.isFinishing || activity.isDestroyed) return@authVerify
|
||||
activity.startActivity(
|
||||
Intent(activity, SplashActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
)
|
||||
activity.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startContentSettings(activity: Activity) {
|
||||
activity.startActivity(
|
||||
Intent(activity, ContentSettingsActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE, true)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun executeAccessDecision(
|
||||
decision: AccessDecision,
|
||||
onAllowed: () -> Unit,
|
||||
onLoginRequired: () -> Unit,
|
||||
onAdultVerificationRequired: () -> Unit,
|
||||
onAdultContentSettingRequired: () -> Unit
|
||||
): Boolean {
|
||||
return when (decision) {
|
||||
AccessDecision.Allowed -> {
|
||||
onAllowed()
|
||||
true
|
||||
}
|
||||
AccessDecision.LoginRequired -> {
|
||||
onLoginRequired()
|
||||
false
|
||||
}
|
||||
AccessDecision.AdultVerificationRequired -> {
|
||||
onAdultVerificationRequired()
|
||||
false
|
||||
}
|
||||
AccessDecision.AdultContentSettingRequired -> {
|
||||
onAdultContentSettingRequired()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.ensureV2Access(
|
||||
requirement: AccessRequirement,
|
||||
onAllowed: () -> Unit = {}
|
||||
): Boolean = V2AccessGuard().ensureAccess(this, requirement, onAllowed)
|
||||
|
||||
fun Fragment.ensureV2Access(
|
||||
requirement: AccessRequirement,
|
||||
onAllowed: () -> Unit = {}
|
||||
): Boolean = V2AccessGuard().ensureAccess(this, requirement, onAllowed)
|
||||
|
||||
fun isV2AccessAllowed(requirement: AccessRequirement): Boolean {
|
||||
return V2AccessGuard().isAllowed(requirement)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package kr.co.vividnext.sodalive.v2.access
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class V2AccessGuardTest {
|
||||
|
||||
@Test
|
||||
fun `각 Access 결과는 대응하는 action만 정확히 한 번 실행한다`() {
|
||||
val cases = listOf(
|
||||
AccessDecision.Allowed to "allowed",
|
||||
AccessDecision.LoginRequired to "login",
|
||||
AccessDecision.AdultVerificationRequired to "adultVerification",
|
||||
AccessDecision.AdultContentSettingRequired to "adultContentSetting"
|
||||
)
|
||||
|
||||
cases.forEach { (decision, expectedAction) ->
|
||||
val executedActions = mutableListOf<String>()
|
||||
|
||||
val isAllowed = executeAccessDecision(
|
||||
decision = decision,
|
||||
onAllowed = { executedActions += "allowed" },
|
||||
onLoginRequired = { executedActions += "login" },
|
||||
onAdultVerificationRequired = { executedActions += "adultVerification" },
|
||||
onAdultContentSettingRequired = { executedActions += "adultContentSetting" }
|
||||
)
|
||||
|
||||
assertEquals(decision == AccessDecision.Allowed, isAllowed)
|
||||
assertEquals(listOf(expectedAction), executedActions)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,27 +8,50 @@ import java.io.File
|
||||
class MainV2LoginGuardSourceTest {
|
||||
|
||||
@Test
|
||||
fun `MainV2 로그인 가드는 v2 Activity 로그인 진입점과 성인 콘텐츠 설정을 사용한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuard.kt").readText()
|
||||
fun `V2AccessGuard는 기존 로그인 본인인증 성인 콘텐츠 설정 이동 계약을 유지한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/access/V2AccessGuard.kt").readText()
|
||||
val policy = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/access/AccessPolicy.kt").readText()
|
||||
val state = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/access/AccessState.kt").readText()
|
||||
val mainV2Activity = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt"
|
||||
).readText()
|
||||
val handleDecisionSource = source.substring(
|
||||
source.indexOf("private fun handleDecision("),
|
||||
source.indexOf("private fun startLoginActivity")
|
||||
)
|
||||
|
||||
assertTrue(source.contains("fun Fragment.ensureMainV2NavigationAllowed("))
|
||||
assertTrue(source.contains("requiresAdultContentAccess: Boolean = false"))
|
||||
assertTrue(source.contains("SharedPreferenceManager.token.isBlank()"))
|
||||
assertTrue(source.contains("(activity as? MainV2Activity)?.showLoginActivity()"))
|
||||
assertTrue(source.contains("SharedPreferenceManager.countryCode.ifBlank { \"KR\" }"))
|
||||
assertTrue(source.contains("SharedPreferenceManager.isAuth"))
|
||||
assertTrue(source.contains("fun Activity.ensureV2Access("))
|
||||
assertTrue(source.contains("fun Fragment.ensureV2Access("))
|
||||
assertTrue(source.contains("fun isV2AccessAllowed(requirement: AccessRequirement): Boolean"))
|
||||
assertTrue(source.contains("fun isAllowed(requirement: AccessRequirement): Boolean"))
|
||||
assertTrue(source.contains("AccessDecision.Allowed"))
|
||||
assertTrue(source.contains("onAllowed()"))
|
||||
assertTrue(handleDecisionSource.contains("onLoginRequired = startLogin"))
|
||||
assertTrue(handleDecisionSource.contains("onAdultVerificationRequired = { showAuthDialog(activity) }"))
|
||||
assertTrue(handleDecisionSource.contains("onAdultContentSettingRequired = { startContentSettings(activity) }"))
|
||||
assertTrue(source.contains("Intent(activity, LoginActivity::class.java)"))
|
||||
assertTrue(source.contains("putExtra(Constants.EXTRA_DATA, activity.intent.extras)"))
|
||||
assertTrue(source.contains("Intent.FLAG_ACTIVITY_CLEAR_TOP"))
|
||||
assertTrue(source.contains("Intent.FLAG_ACTIVITY_SINGLE_TOP"))
|
||||
assertTrue(source.contains("V2ModalDialog("))
|
||||
assertFalse(source.contains("SodaDialog("))
|
||||
assertTrue(source.contains("Auth.auth(requireActivity(), requireContext())"))
|
||||
assertTrue(source.contains("Auth.auth(activity, activity)"))
|
||||
assertTrue(source.contains("Gson().fromJson(json, BootpayResponse::class.java)"))
|
||||
assertTrue(source.contains("AuthVerifyRequest(receiptId = bootpayResponse.data.receiptId)"))
|
||||
assertTrue(source.contains("myPageViewModel.authVerify(request)"))
|
||||
assertTrue(source.contains("if (activity.isFinishing || activity.isDestroyed)"))
|
||||
assertTrue(source.contains("SplashActivity::class.java"))
|
||||
assertTrue(source.contains("SharedPreferenceManager.isAdultContentVisible"))
|
||||
assertTrue(source.contains("Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK"))
|
||||
assertTrue(source.contains("ContentSettingsActivity::class.java"))
|
||||
assertTrue(source.contains("putExtra(Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE, true)"))
|
||||
assertFalse(source.contains("Auth.auth(requireActivity(), requireContext()) {}"))
|
||||
assertFalse(source.contains("kr.co.vividnext.sodalive.main.MainActivity"))
|
||||
assertFalse(source.contains("Auth.auth(activity, activity) {}"))
|
||||
assertTrue(policy.contains("state.token.isNotBlank() && state.token.length > LOGIN_TOKEN_MIN_LENGTH"))
|
||||
assertTrue(policy.contains("state.countryCode.ifBlank { \"KR\" } == \"KR\""))
|
||||
assertTrue(state.contains("SharedPreferenceManager.token"))
|
||||
assertTrue(state.contains("SharedPreferenceManager.isAuth"))
|
||||
assertTrue(state.contains("SharedPreferenceManager.isAdultContentVisible"))
|
||||
assertTrue(mainV2Activity.contains("fun showLoginActivity()"))
|
||||
assertTrue(mainV2Activity.contains("ensureV2Access(AccessRequirement.Login)"))
|
||||
}
|
||||
|
||||
private fun projectFile(relativePath: String): File {
|
||||
|
||||
Reference in New Issue
Block a user