refactor(live): 공통 액션 코디네이터를 추가한다
This commit is contained in:
@@ -0,0 +1,392 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.live.action
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
|
||||||
|
import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerService
|
||||||
|
import kr.co.vividnext.sodalive.common.Constants
|
||||||
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
|
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||||
|
import kr.co.vividnext.sodalive.live.LiveViewModel
|
||||||
|
import kr.co.vividnext.sodalive.live.reservation.complete.LiveReservationCompleteActivity
|
||||||
|
import kr.co.vividnext.sodalive.live.room.LiveRoomActivity
|
||||||
|
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
|
||||||
|
import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailFragment
|
||||||
|
import kr.co.vividnext.sodalive.live.room.dialog.LiveCancelDialog
|
||||||
|
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.update.LiveRoomEditActivity
|
||||||
|
import kr.co.vividnext.sodalive.settings.language.LanguageManager
|
||||||
|
import kr.co.vividnext.sodalive.settings.language.LocaleHelper
|
||||||
|
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||||
|
import kr.co.vividnext.sodalive.v2.access.ensureV2Access
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.TimeZone
|
||||||
|
|
||||||
|
class LiveActionCoordinator(
|
||||||
|
private val activity: FragmentActivity,
|
||||||
|
private val layoutInflater: LayoutInflater,
|
||||||
|
private val fragmentManager: FragmentManager,
|
||||||
|
private val liveViewModel: LiveViewModel,
|
||||||
|
private val screenWidthProvider: () -> Int,
|
||||||
|
private val refreshHome: () -> Unit,
|
||||||
|
private val canHandleResult: () -> Boolean = { !activity.isFinishing && !activity.isDestroyed }
|
||||||
|
) {
|
||||||
|
fun showLiveRoomDetail(roomId: Long) {
|
||||||
|
activity.ensureV2Access(AccessRequirement.Login) {
|
||||||
|
liveViewModel.getRoomDetail(roomId) { roomDetail ->
|
||||||
|
if (!canHandleUiResult()) return@getRoomDetail
|
||||||
|
|
||||||
|
activity.ensureV2Access(AccessRequirement.forAdultContent(roomDetail.isAdult)) {
|
||||||
|
showLiveRoomDetailContent(roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showLiveRoomDetailContent(roomId: Long) {
|
||||||
|
if (!canShowLiveRoomDetail(
|
||||||
|
canHandleUiResult = ::canHandleUiResult,
|
||||||
|
existingDetailFragment = fragmentManager.findFragmentByTag(LIVE_ROOM_DETAIL_TAG)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val detailFragment = LiveRoomDetailFragment(
|
||||||
|
roomId,
|
||||||
|
onClickParticipant = {},
|
||||||
|
onClickReservation = { reservationRoom(roomId) },
|
||||||
|
onClickModify = { roomDetailResponse -> modifyLive(roomDetailResponse) },
|
||||||
|
onClickStart = { startLive(roomId) },
|
||||||
|
onClickCancel = { cancelLive(roomId) }
|
||||||
|
)
|
||||||
|
|
||||||
|
detailFragment.showNow(fragmentManager, LIVE_ROOM_DETAIL_TAG)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enterLiveRoom(
|
||||||
|
roomId: Long,
|
||||||
|
requiresAdultContentAccess: Boolean? = null,
|
||||||
|
onUnavailable: (() -> Unit)? = null
|
||||||
|
) {
|
||||||
|
val handleRoomDetail: (GetRoomDetailResponse) -> Unit = handleRoomDetail@{ roomDetail ->
|
||||||
|
if (!canHandleUiResult()) return@handleRoomDetail
|
||||||
|
|
||||||
|
val decision = decideLiveEntry(roomDetail)
|
||||||
|
val requiresAdultAccess = requiresAdultContentAccess == true || roomDetail.isAdult
|
||||||
|
|
||||||
|
runLiveEntryDecisionFlow(
|
||||||
|
decision = decision,
|
||||||
|
onShowDetail = {
|
||||||
|
handleUnavailableLive(roomId, requiresAdultAccess, onUnavailable)
|
||||||
|
},
|
||||||
|
ensureAdultAccess = { onAllowed ->
|
||||||
|
activity.ensureV2Access(
|
||||||
|
AccessRequirement.forAdultContent(requiresAdultAccess),
|
||||||
|
onAllowed
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onAllowedEntry = {
|
||||||
|
if (canHandleUiResult()) {
|
||||||
|
stopAudioPlayback()
|
||||||
|
val onEnterRoomSuccess = {
|
||||||
|
activity.runOnUiThread {
|
||||||
|
runLiveActionUiSideEffect(::canHandleUiResult) {
|
||||||
|
openLiveRoom(roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleLiveEntry(roomId, roomDetail, decision, onEnterRoomSuccess)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val loadRoomDetail = { liveViewModel.getRoomDetail(roomId, handleRoomDetail) }
|
||||||
|
|
||||||
|
activity.ensureV2Access(AccessRequirement.Login, loadRoomDetail)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopAudioPlayback() {
|
||||||
|
activity.startService(
|
||||||
|
Intent(activity, AudioContentPlayService::class.java).apply {
|
||||||
|
action = AudioContentPlayService.MusicAction.STOP.name
|
||||||
|
}
|
||||||
|
)
|
||||||
|
activity.startService(
|
||||||
|
Intent(activity, AudioContentPlayerService::class.java).apply {
|
||||||
|
action = "STOP_SERVICE"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleLiveEntry(
|
||||||
|
roomId: Long,
|
||||||
|
roomDetail: GetRoomDetailResponse,
|
||||||
|
decision: LiveEntryDecision,
|
||||||
|
onEnterRoomSuccess: () -> Unit
|
||||||
|
) {
|
||||||
|
if (!canHandleUiResult()) return
|
||||||
|
|
||||||
|
when (decision) {
|
||||||
|
LiveEntryDecision.ShowDetail -> Unit
|
||||||
|
LiveEntryDecision.Enter -> liveViewModel.enterRoom(roomId, onEnterRoomSuccess)
|
||||||
|
is LiveEntryDecision.RequestPassword -> {
|
||||||
|
LiveRoomPasswordDialog(
|
||||||
|
activity = activity,
|
||||||
|
layoutInflater = layoutInflater,
|
||||||
|
can = decision.can,
|
||||||
|
confirmButtonClick = { password ->
|
||||||
|
liveViewModel.enterRoom(
|
||||||
|
roomId = roomId,
|
||||||
|
onSuccess = onEnterRoomSuccess,
|
||||||
|
password = password
|
||||||
|
)
|
||||||
|
}
|
||||||
|
).show(screenWidthProvider())
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveEntryDecision.RequestPayment -> {
|
||||||
|
showPaidLiveEntryDialog(
|
||||||
|
roomId = roomId,
|
||||||
|
beginDateTimeUtc = roomDetail.beginDateTimeUtc,
|
||||||
|
price = roomDetail.price,
|
||||||
|
onEnterRoomSuccess = onEnterRoomSuccess
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decideLiveEntry(roomDetail: GetRoomDetailResponse): LiveEntryDecision {
|
||||||
|
return LiveEntryPolicy.decide(
|
||||||
|
LiveEntryContext(
|
||||||
|
channelName = roomDetail.channelName,
|
||||||
|
managerId = roomDetail.manager.id,
|
||||||
|
currentUserId = SharedPreferenceManager.userId,
|
||||||
|
price = roomDetail.price,
|
||||||
|
isPaid = roomDetail.isPaid,
|
||||||
|
isPrivateRoom = roomDetail.isPrivateRoom
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleUnavailableLive(
|
||||||
|
roomId: Long,
|
||||||
|
requiresAdultAccess: Boolean,
|
||||||
|
onUnavailable: (() -> Unit)?
|
||||||
|
) {
|
||||||
|
if (onUnavailable != null) {
|
||||||
|
onUnavailable()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
activity.ensureV2Access(AccessRequirement.forAdultContent(requiresAdultAccess)) {
|
||||||
|
showLiveRoomDetailContent(roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reservationRoom(roomId: Long) {
|
||||||
|
liveViewModel.getRoomDetail(roomId) {
|
||||||
|
if (!canHandleUiResult()) return@getRoomDetail
|
||||||
|
|
||||||
|
if (it.manager.id == SharedPreferenceManager.userId) {
|
||||||
|
Toast.makeText(
|
||||||
|
activity,
|
||||||
|
activity.getString(R.string.screen_live_reservation_self_block),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
return@getRoomDetail
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it.isPrivateRoom) {
|
||||||
|
LiveRoomPasswordDialog(
|
||||||
|
activity = activity,
|
||||||
|
layoutInflater = layoutInflater,
|
||||||
|
can = if (it.isPaid) 0 else it.price,
|
||||||
|
confirmButtonClick = { password -> processLiveReservation(roomId, password) }
|
||||||
|
).show(screenWidthProvider())
|
||||||
|
} else if (it.price == 0 || it.isPaid) {
|
||||||
|
processLiveReservation(roomId)
|
||||||
|
} else {
|
||||||
|
LivePaymentDialog(
|
||||||
|
activity = activity,
|
||||||
|
layoutInflater = layoutInflater,
|
||||||
|
title = activity.getString(
|
||||||
|
R.string.screen_live_reservation_pay_title,
|
||||||
|
it.price.moneyFormat()
|
||||||
|
),
|
||||||
|
desc = activity.getString(R.string.screen_live_reservation_pay_desc, it.title),
|
||||||
|
confirmButtonTitle = activity.getString(R.string.screen_live_reservation_confirm),
|
||||||
|
confirmButtonClick = { processLiveReservation(roomId) },
|
||||||
|
cancelButtonTitle = activity.getString(R.string.cancel),
|
||||||
|
cancelButtonClick = {}
|
||||||
|
).show(screenWidthProvider())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processLiveReservation(roomId: Long, password: String? = null) {
|
||||||
|
liveViewModel.reservationRoom(roomId, password) {
|
||||||
|
if (!canHandleUiResult()) return@reservationRoom
|
||||||
|
|
||||||
|
refreshHome()
|
||||||
|
activity.startActivity(
|
||||||
|
Intent(activity, LiveReservationCompleteActivity::class.java).apply {
|
||||||
|
putExtra(Constants.EXTRA_LIVE_RESERVATION_RESPONSE, it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun modifyLive(roomDetail: GetRoomDetailResponse) {
|
||||||
|
if (!canHandleUiResult()) return
|
||||||
|
|
||||||
|
activity.startActivity(
|
||||||
|
Intent(activity, LiveRoomEditActivity::class.java).apply {
|
||||||
|
putExtra(Constants.EXTRA_ROOM_DETAIL, roomDetail)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startLive(roomId: Long) {
|
||||||
|
liveViewModel.startLive(roomId) {
|
||||||
|
if (!canHandleUiResult()) return@startLive
|
||||||
|
|
||||||
|
refreshHome()
|
||||||
|
activity.runOnUiThread {
|
||||||
|
runLiveActionUiSideEffect(::canHandleUiResult) {
|
||||||
|
openLiveRoom(roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancelLive(roomId: Long) {
|
||||||
|
if (!canHandleUiResult()) return
|
||||||
|
|
||||||
|
LiveCancelDialog(
|
||||||
|
activity = activity,
|
||||||
|
layoutInflater = layoutInflater,
|
||||||
|
title = activity.getString(R.string.screen_live_cancel_title),
|
||||||
|
hint = activity.getString(R.string.screen_live_cancel_hint),
|
||||||
|
confirmButtonTitle = activity.getString(R.string.screen_live_cancel_confirm),
|
||||||
|
confirmButtonClick = { reason ->
|
||||||
|
liveViewModel.cancelLive(roomId, reason) {
|
||||||
|
if (!canHandleUiResult()) return@cancelLive
|
||||||
|
|
||||||
|
Toast.makeText(
|
||||||
|
activity,
|
||||||
|
activity.getString(R.string.screen_live_cancel_success),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
refreshHome()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancelButtonTitle = activity.getString(R.string.dialog_close),
|
||||||
|
cancelButtonClick = {}
|
||||||
|
).show(screenWidthProvider())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showPaidLiveEntryDialog(
|
||||||
|
roomId: Long,
|
||||||
|
beginDateTimeUtc: String,
|
||||||
|
price: Int,
|
||||||
|
onEnterRoomSuccess: () -> Unit
|
||||||
|
) {
|
||||||
|
if (!canHandleUiResult()) return
|
||||||
|
|
||||||
|
val locale = Locale(LanguageManager.getEffectiveLanguage(activity))
|
||||||
|
val wrappedContext = LocaleHelper.wrap(activity)
|
||||||
|
val beginDate = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).apply {
|
||||||
|
timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
}.parse(beginDateTimeUtc) ?: return
|
||||||
|
val now = Date()
|
||||||
|
val dateFormat = SimpleDateFormat("yyyy-MM-dd, HH:mm", locale)
|
||||||
|
val diffTime = now.time - beginDate.time
|
||||||
|
val hours = (diffTime / (1000 * 60 * 60)).toInt()
|
||||||
|
val mins = (diffTime / (1000 * 60)).toInt() % 60
|
||||||
|
|
||||||
|
LivePaymentDialog(
|
||||||
|
activity = activity,
|
||||||
|
layoutInflater = layoutInflater,
|
||||||
|
title = wrappedContext.getString(R.string.live_paid_title),
|
||||||
|
startDateTime = if (hours >= 1) dateFormat.format(beginDate) else null,
|
||||||
|
nowDateTime = if (hours >= 1) dateFormat.format(now) else null,
|
||||||
|
desc = wrappedContext.getString(R.string.live_paid_desc, price),
|
||||||
|
desc2 = if (hours >= 1) {
|
||||||
|
wrappedContext.getString(R.string.live_paid_warning, hours, mins)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
|
confirmButtonTitle = wrappedContext.getString(R.string.live_paid_confirm),
|
||||||
|
confirmButtonClick = { liveViewModel.enterRoom(roomId, onEnterRoomSuccess) },
|
||||||
|
cancelButtonTitle = wrappedContext.getString(R.string.cancel),
|
||||||
|
cancelButtonClick = {}
|
||||||
|
).show(screenWidthProvider())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openLiveRoom(roomId: Long) {
|
||||||
|
activity.startActivity(
|
||||||
|
Intent(activity, LiveRoomActivity::class.java).apply {
|
||||||
|
putExtra(Constants.EXTRA_ROOM_ID, roomId)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun canHandleUiResult(): Boolean {
|
||||||
|
return canHandleLiveActionUiResult(
|
||||||
|
isActivityFinishing = activity.isFinishing,
|
||||||
|
isActivityDestroyed = activity.isDestroyed,
|
||||||
|
isStateSaved = fragmentManager.isStateSaved,
|
||||||
|
canHandleResult = canHandleResult
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val LIVE_ROOM_DETAIL_TAG = "LiveRoomDetailFragment"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun runLiveActionUiSideEffect(
|
||||||
|
canHandleResult: () -> Boolean,
|
||||||
|
sideEffect: () -> Unit
|
||||||
|
): Boolean {
|
||||||
|
if (!canHandleResult()) return false
|
||||||
|
sideEffect()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun runLiveEntryDecisionFlow(
|
||||||
|
decision: LiveEntryDecision,
|
||||||
|
onShowDetail: () -> Unit,
|
||||||
|
ensureAdultAccess: (onAllowed: () -> Unit) -> Unit,
|
||||||
|
onAllowedEntry: () -> Unit
|
||||||
|
) {
|
||||||
|
if (decision == LiveEntryDecision.ShowDetail) {
|
||||||
|
onShowDetail()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureAdultAccess(onAllowedEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun canHandleLiveActionUiResult(
|
||||||
|
isActivityFinishing: Boolean,
|
||||||
|
isActivityDestroyed: Boolean,
|
||||||
|
isStateSaved: Boolean,
|
||||||
|
canHandleResult: () -> Boolean
|
||||||
|
): Boolean {
|
||||||
|
return !isActivityFinishing && !isActivityDestroyed && !isStateSaved && canHandleResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun canShowLiveRoomDetail(
|
||||||
|
canHandleUiResult: () -> Boolean,
|
||||||
|
existingDetailFragment: Any?
|
||||||
|
): Boolean {
|
||||||
|
return canHandleUiResult() && existingDetailFragment == null
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user