parent
6c9ace146d
commit
e964679154
|
@ -35,8 +35,8 @@ android {
|
||||||
applicationId "kr.co.vividnext.sodalive"
|
applicationId "kr.co.vividnext.sodalive"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 117
|
versionCode 118
|
||||||
versionName "1.21.2"
|
versionName "1.22.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
@ -159,4 +159,6 @@ dependencies {
|
||||||
kapt "androidx.room:room-compiler:2.5.0"
|
kapt "androidx.room:room-compiler:2.5.0"
|
||||||
implementation "androidx.room:room-ktx:2.5.0"
|
implementation "androidx.room:room-ktx:2.5.0"
|
||||||
implementation "androidx.room:room-runtime:2.5.0"
|
implementation "androidx.room:room-runtime:2.5.0"
|
||||||
|
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,9 @@ import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationRequest
|
||||||
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationResponse
|
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationResponse
|
||||||
import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse
|
import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse
|
||||||
import kr.co.vividnext.sodalive.live.room.kick_out.LiveRoomKickOutRequest
|
import kr.co.vividnext.sodalive.live.room.kick_out.LiveRoomKickOutRequest
|
||||||
|
import kr.co.vividnext.sodalive.live.room.like.LiveRoomLikeHeartRequest
|
||||||
import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse
|
import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse
|
||||||
import kr.co.vividnext.sodalive.live.room.tag.GetLiveTagResponse
|
import kr.co.vividnext.sodalive.live.room.tag.GetLiveTagResponse
|
||||||
import kr.co.vividnext.sodalive.settings.ContentType
|
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
|
@ -213,4 +213,10 @@ interface LiveApi {
|
||||||
@Body request: CancelLiveReservationRequest,
|
@Body request: CancelLiveReservationRequest,
|
||||||
@Header("Authorization") authHeader: String
|
@Header("Authorization") authHeader: String
|
||||||
): Single<ApiResponse<Any>>
|
): Single<ApiResponse<Any>>
|
||||||
|
|
||||||
|
@POST("/live/room/like-heart")
|
||||||
|
fun likeHeart(
|
||||||
|
@Body request: LiveRoomLikeHeartRequest,
|
||||||
|
@Header("Authorization") authHeader: String
|
||||||
|
): Single<ApiResponse<Any>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import kr.co.vividnext.sodalive.live.room.donation.DeleteLiveRoomDonationMessage
|
||||||
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationRequest
|
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationRequest
|
||||||
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationResponse
|
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationResponse
|
||||||
import kr.co.vividnext.sodalive.live.room.kick_out.LiveRoomKickOutRequest
|
import kr.co.vividnext.sodalive.live.room.kick_out.LiveRoomKickOutRequest
|
||||||
|
import kr.co.vividnext.sodalive.live.room.like.LiveRoomLikeHeartRequest
|
||||||
import kr.co.vividnext.sodalive.live.room.menu.MenuApi
|
import kr.co.vividnext.sodalive.live.room.menu.MenuApi
|
||||||
import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest
|
import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest
|
||||||
import kr.co.vividnext.sodalive.user.UserApi
|
import kr.co.vividnext.sodalive.user.UserApi
|
||||||
|
@ -242,4 +243,12 @@ class LiveRepository(
|
||||||
creatorId = creatorId,
|
creatorId = creatorId,
|
||||||
authHeader = token
|
authHeader = token
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun likeHeart(roomId: Long, token: String) = api.likeHeart(
|
||||||
|
request = LiveRoomLikeHeartRequest(
|
||||||
|
roomId = roomId,
|
||||||
|
container = "aos"
|
||||||
|
),
|
||||||
|
authHeader = token
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
|
@ -53,6 +54,7 @@ import io.agora.rtm.RtmChannelMember
|
||||||
import io.agora.rtm.RtmClientListener
|
import io.agora.rtm.RtmClientListener
|
||||||
import io.agora.rtm.RtmMessage
|
import io.agora.rtm.RtmMessage
|
||||||
import io.agora.rtm.RtmMessageType
|
import io.agora.rtm.RtmMessageType
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.agora.Agora
|
import kr.co.vividnext.sodalive.agora.Agora
|
||||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
|
@ -124,6 +126,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||||
private var isMicrophoneMute = false
|
private var isMicrophoneMute = false
|
||||||
private var isSpeaker = false
|
private var isSpeaker = false
|
||||||
|
|
||||||
|
private var isHost = false
|
||||||
private var isNoChatting = false
|
private var isNoChatting = false
|
||||||
private var remainingNoChattingTime = noChattingTime
|
private var remainingNoChattingTime = noChattingTime
|
||||||
|
|
||||||
|
@ -530,8 +533,13 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||||
return (second * 1000).toLong()
|
return (second * 1000).toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addHeartAnimation(button: View) {
|
private fun addHeartAnimation() {
|
||||||
// 버튼의 위치
|
// 버튼의 위치
|
||||||
|
val button = if (isHost) {
|
||||||
|
binding.flRouletteSettings
|
||||||
|
} else {
|
||||||
|
binding.flLikeHeart
|
||||||
|
}
|
||||||
val buttonPosition = IntArray(2)
|
val buttonPosition = IntArray(2)
|
||||||
button.getLocationInWindow(buttonPosition)
|
button.getLocationInWindow(buttonPosition)
|
||||||
|
|
||||||
|
@ -1004,12 +1012,10 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||||
binding.ivCreatorFollow.visibility = View.GONE
|
binding.ivCreatorFollow.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
initLikeHeartButton(isHost = response.creatorId == SharedPreferenceManager.userId)
|
isHost = response.creatorId == SharedPreferenceManager.userId
|
||||||
initRouletteSettingButton(isHost = response.creatorId == SharedPreferenceManager.userId)
|
initLikeHeartButton()
|
||||||
activatingRouletteButton(
|
initRouletteSettingButton()
|
||||||
isHost = response.creatorId == SharedPreferenceManager.userId,
|
activatingRouletteButton(isActiveRoulette = response.isActiveRoulette)
|
||||||
isActiveRoulette = response.isActiveRoulette
|
|
||||||
)
|
|
||||||
|
|
||||||
if (response.menuPan.isNotBlank()) {
|
if (response.menuPan.isNotBlank()) {
|
||||||
binding.tvMenuPan.visibility = View.VISIBLE
|
binding.tvMenuPan.visibility = View.VISIBLE
|
||||||
|
@ -1094,16 +1100,51 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initLikeHeartButton(isHost: Boolean) {
|
private fun initLikeHeartButton() {
|
||||||
if (!isHost) {
|
if (!isHost) {
|
||||||
binding.flLikeHeart.visibility = View.VISIBLE
|
binding.flLikeHeart.visibility = View.VISIBLE
|
||||||
binding.flLikeHeart.setOnClickListener { addHeartAnimation(it) }
|
binding.flLikeHeart.setOnClickListener {
|
||||||
|
binding.flLikeHeart.isEnabled = false
|
||||||
|
viewModel.likeHeart(
|
||||||
|
roomId = roomId,
|
||||||
|
onSuccess = {
|
||||||
|
val donationRawMessage = Gson().toJson(
|
||||||
|
LiveRoomChatRawMessage(
|
||||||
|
type = LiveRoomChatRawMessageType.HEART_DONATION,
|
||||||
|
message = "",
|
||||||
|
can = 1,
|
||||||
|
signature = null,
|
||||||
|
signatureImageUrl = null,
|
||||||
|
donationMessage = null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
agora.sendRawMessageToGroup(
|
||||||
|
rawMessage = donationRawMessage.toByteArray(),
|
||||||
|
onSuccess = {
|
||||||
|
handler.post {
|
||||||
|
addHeartAnimation()
|
||||||
|
lifecycleScope.launch { viewModel.addHeartDonation() }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
viewModel.refundDonation(roomId)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.flLikeHeart.isEnabled = true
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
binding.flLikeHeart.isEnabled = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
binding.flLikeHeart.visibility = View.GONE
|
binding.flLikeHeart.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initRouletteSettingButton(isHost: Boolean) {
|
private fun initRouletteSettingButton() {
|
||||||
if (isHost) {
|
if (isHost) {
|
||||||
binding.flRouletteSettings.visibility = View.VISIBLE
|
binding.flRouletteSettings.visibility = View.VISIBLE
|
||||||
binding.flRouletteSettings.setOnClickListener {
|
binding.flRouletteSettings.setOnClickListener {
|
||||||
|
@ -1119,7 +1160,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun activatingRouletteButton(isHost: Boolean, isActiveRoulette: Boolean) {
|
private fun activatingRouletteButton(isActiveRoulette: Boolean) {
|
||||||
if (!isHost && isActiveRoulette) {
|
if (!isHost && isActiveRoulette) {
|
||||||
binding.flRoulette.visibility = View.VISIBLE
|
binding.flRoulette.visibility = View.VISIBLE
|
||||||
binding.flRoulette.setOnClickListener {
|
binding.flRoulette.setOnClickListener {
|
||||||
|
@ -1533,7 +1574,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
invalidateChat()
|
invalidateChat()
|
||||||
viewModel.addDonationCan(can)
|
lifecycleScope.launch { viewModel.addDonationCan(can) }
|
||||||
addSignature(signature)
|
addSignature(signature)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1574,7 +1615,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
invalidateChat()
|
invalidateChat()
|
||||||
viewModel.addDonationCan(can)
|
lifecycleScope.launch { viewModel.addDonationCan(can) }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onFailure = {
|
onFailure = {
|
||||||
|
@ -1641,7 +1682,9 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
invalidateChat()
|
invalidateChat()
|
||||||
|
lifecycleScope.launch {
|
||||||
viewModel.addDonationCan(rawMessage.can)
|
viewModel.addDonationCan(rawMessage.can)
|
||||||
|
}
|
||||||
|
|
||||||
if (rawMessage.signature != null) {
|
if (rawMessage.signature != null) {
|
||||||
addSignature(rawMessage.signature)
|
addSignature(rawMessage.signature)
|
||||||
|
@ -1666,9 +1709,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||||
LiveRoomChatRawMessageType.TOGGLE_ROULETTE -> {
|
LiveRoomChatRawMessageType.TOGGLE_ROULETTE -> {
|
||||||
handler.post {
|
handler.post {
|
||||||
activatingRouletteButton(
|
activatingRouletteButton(
|
||||||
isHost = viewModel
|
|
||||||
.roomInfoResponse
|
|
||||||
.creatorId == SharedPreferenceManager.userId,
|
|
||||||
isActiveRoulette = rawMessage.isActiveRoulette ?: false
|
isActiveRoulette = rawMessage.isActiveRoulette ?: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1684,9 +1724,18 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
invalidateChat()
|
invalidateChat()
|
||||||
|
lifecycleScope.launch {
|
||||||
viewModel.addDonationCan(rawMessage.can)
|
viewModel.addDonationCan(rawMessage.can)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveRoomChatRawMessageType.HEART_DONATION -> {
|
||||||
|
handler.post {
|
||||||
|
addHeartAnimation()
|
||||||
|
lifecycleScope.launch { viewModel.addHeartDonation() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
@ -2009,7 +2058,9 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
invalidateChat()
|
invalidateChat()
|
||||||
|
lifecycleScope.launch {
|
||||||
viewModel.addDonationCan(message.can)
|
viewModel.addDonationCan(message.can)
|
||||||
|
}
|
||||||
|
|
||||||
if (message.signature != null) {
|
if (message.signature != null) {
|
||||||
addSignature(message.signature)
|
addSignature(message.signature)
|
||||||
|
|
|
@ -13,6 +13,8 @@ import com.google.gson.Gson
|
||||||
import com.orhanobut.logger.Logger
|
import com.orhanobut.logger.Logger
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.live.LiveRepository
|
import kr.co.vividnext.sodalive.live.LiveRepository
|
||||||
|
@ -100,6 +102,8 @@ class LiveRoomViewModel(
|
||||||
|
|
||||||
private val blockedMemberIdList: MutableList<Long> = mutableListOf()
|
private val blockedMemberIdList: MutableList<Long> = mutableListOf()
|
||||||
|
|
||||||
|
val mutex = Mutex()
|
||||||
|
|
||||||
fun getUserNickname(memberId: Int): String {
|
fun getUserNickname(memberId: Int): String {
|
||||||
for (manager in roomInfoResponse.managerList) {
|
for (manager in roomInfoResponse.managerList) {
|
||||||
if (manager.id.toInt() == memberId) {
|
if (manager.id.toInt() == memberId) {
|
||||||
|
@ -562,6 +566,36 @@ class LiveRoomViewModel(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun likeHeart(roomId: Long, onSuccess: () -> Unit, onFailure: () -> Unit) {
|
||||||
|
compositeDisposable.add(
|
||||||
|
repository.likeHeart(roomId, token = "Bearer ${SharedPreferenceManager.token}")
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(
|
||||||
|
{
|
||||||
|
if (it.success) {
|
||||||
|
SharedPreferenceManager.can -= 1
|
||||||
|
onSuccess()
|
||||||
|
} else {
|
||||||
|
if (it.message != null) {
|
||||||
|
_toastLiveData.postValue(it.message)
|
||||||
|
} else {
|
||||||
|
_toastLiveData.postValue(
|
||||||
|
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onFailure()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
it.message?.let { message -> Logger.e(message) }
|
||||||
|
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||||
|
onFailure()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun donation(
|
fun donation(
|
||||||
roomId: Long,
|
roomId: Long,
|
||||||
can: Int,
|
can: Int,
|
||||||
|
@ -641,9 +675,17 @@ class LiveRoomViewModel(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addDonationCan(can: Int) {
|
suspend fun addDonationCan(can: Int) {
|
||||||
|
mutex.withLock {
|
||||||
_totalDonationCan.postValue(totalDonationCan.value!! + can)
|
_totalDonationCan.postValue(totalDonationCan.value!! + can)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun addHeartDonation() {
|
||||||
|
mutex.withLock {
|
||||||
|
_totalLikeHeart.postValue(totalLikeHeart.value!! + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun donationStatus(roomId: Long, onSuccess: (GetLiveRoomDonationStatusResponse) -> Unit) {
|
fun donationStatus(roomId: Long, onSuccess: (GetLiveRoomDonationStatusResponse) -> Unit) {
|
||||||
_isLoading.value = true
|
_isLoading.value = true
|
||||||
|
|
|
@ -35,5 +35,8 @@ enum class LiveRoomChatRawMessageType {
|
||||||
TOGGLE_ROULETTE,
|
TOGGLE_ROULETTE,
|
||||||
|
|
||||||
@SerializedName("ROULETTE_DONATION")
|
@SerializedName("ROULETTE_DONATION")
|
||||||
ROULETTE_DONATION
|
ROULETTE_DONATION,
|
||||||
|
|
||||||
|
@SerializedName("HEART_DONATION")
|
||||||
|
HEART_DONATION
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.room.like
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class LiveRoomLikeHeartRequest(
|
||||||
|
@SerializedName("roomId") val roomId: Long,
|
||||||
|
@SerializedName("container") val container: String
|
||||||
|
)
|
Loading…
Reference in New Issue