라이브 방 추가
|
@ -50,6 +50,8 @@ android {
|
|||
|
||||
buildConfigField 'String', 'BASE_URL', '"https://api.sodalive.net"'
|
||||
buildConfigField 'String', 'BOOTPAY_APP_ID', '"64c35be1d25985001dc50c87"'
|
||||
buildConfigField 'String', 'AGORA_APP_ID', '"4566e5b98f434b9eabb63435f794a1f9"'
|
||||
buildConfigField 'String', 'AGORA_APP_CERTIFICATE', '"e89e30f9ee584fda9454db3c0e387632"'
|
||||
}
|
||||
|
||||
debug {
|
||||
|
@ -59,6 +61,8 @@ android {
|
|||
|
||||
buildConfigField 'String', 'BASE_URL', '"https://test-api.sodalive.net"'
|
||||
buildConfigField 'String', 'BOOTPAY_APP_ID', '"6242a7772701800023f68b2e"'
|
||||
buildConfigField 'String', 'AGORA_APP_ID', '"d28c80855d314a599cd7c15280920699"'
|
||||
buildConfigField 'String', 'AGORA_APP_CERTIFICATE', '"29ef33b7c37e4b80b74af9a6e9b2af5e"'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
|
@ -131,4 +135,8 @@ dependencies {
|
|||
|
||||
// bootpay
|
||||
implementation "io.github.bootpay:android:4.3.4"
|
||||
|
||||
// agora
|
||||
implementation "io.agora.rtc:voice-sdk:4.1.0-1"
|
||||
implementation 'io.agora.rtm:rtm-sdk:1.5.3'
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
<activity android:name=".live.room.create.LiveRoomCreateActivity" />
|
||||
<activity android:name=".live.room.update.LiveRoomEditActivity" />
|
||||
<activity android:name=".live.reservation.complete.LiveReservationCompleteActivity" />
|
||||
<activity android:name=".live.room.LiveRoomActivity" />
|
||||
|
||||
<activity
|
||||
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
package kr.co.vividnext.sodalive.agora
|
||||
|
||||
import android.content.Context
|
||||
import com.orhanobut.logger.Logger
|
||||
import io.agora.rtc2.Constants
|
||||
import io.agora.rtc2.IRtcEngineEventHandler
|
||||
import io.agora.rtc2.RtcEngine
|
||||
import io.agora.rtm.ErrorInfo
|
||||
import io.agora.rtm.ResultCallback
|
||||
import io.agora.rtm.RtmChannel
|
||||
import io.agora.rtm.RtmChannelListener
|
||||
import io.agora.rtm.RtmClient
|
||||
import io.agora.rtm.RtmClientListener
|
||||
import io.agora.rtm.SendMessageOptions
|
||||
import kr.co.vividnext.sodalive.BuildConfig
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomRequestType
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class Agora(
|
||||
private val context: Context,
|
||||
private val rtcEventHandler: IRtcEngineEventHandler,
|
||||
private val rtmClientListener: RtmClientListener
|
||||
) {
|
||||
// RTM client instance
|
||||
private var rtmClient: RtmClient? = null
|
||||
|
||||
// RTM channel instance
|
||||
private var rtmChannel: RtmChannel? = null
|
||||
|
||||
private var rtcEngine: RtcEngine? = null
|
||||
|
||||
init {
|
||||
initAgoraEngine()
|
||||
}
|
||||
|
||||
private fun initAgoraEngine() {
|
||||
try {
|
||||
rtcEngine = RtcEngine.create(
|
||||
context,
|
||||
BuildConfig.AGORA_APP_ID,
|
||||
rtcEventHandler
|
||||
)
|
||||
|
||||
rtcEngine!!.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING)
|
||||
rtcEngine!!.setAudioProfile(
|
||||
Constants.AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO,
|
||||
Constants.AUDIO_SCENARIO_GAME_STREAMING
|
||||
)
|
||||
rtcEngine!!.enableAudio()
|
||||
rtcEngine!!.enableAudioVolumeIndication(500, 3, true)
|
||||
|
||||
rtmClient = RtmClient.createInstance(
|
||||
context,
|
||||
BuildConfig.AGORA_APP_ID,
|
||||
rtmClientListener
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun deInitAgoraEngine() {
|
||||
if (rtcEngine != null) {
|
||||
rtcEngine!!.leaveChannel()
|
||||
|
||||
thread {
|
||||
RtcEngine.destroy()
|
||||
rtcEngine = null
|
||||
}
|
||||
}
|
||||
|
||||
rtmChannel?.leave(null)
|
||||
rtmChannel?.release()
|
||||
rtmClient?.logout(null)
|
||||
}
|
||||
|
||||
fun inputChat(message: String) {
|
||||
val rtmMessage = rtmClient!!.createMessage()
|
||||
rtmMessage.text = message
|
||||
|
||||
rtmChannel!!.sendMessage(
|
||||
rtmMessage,
|
||||
object : ResultCallback<Void?> {
|
||||
override fun onSuccess(p0: Void?) {
|
||||
Logger.e("sendMessage - onSuccess")
|
||||
}
|
||||
|
||||
override fun onFailure(p0: ErrorInfo) {
|
||||
Logger.e("sendMessage fail - ${p0.errorCode}")
|
||||
Logger.e("sendMessage fail - ${p0.errorDescription}")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun joinRtcChannel(uid: Int, rtcToken: String, channelName: String) {
|
||||
rtcEngine!!.joinChannel(
|
||||
rtcToken,
|
||||
channelName,
|
||||
"",
|
||||
uid
|
||||
)
|
||||
}
|
||||
|
||||
fun createRtmChannelAndLogin(
|
||||
uid: String,
|
||||
rtmToken: String,
|
||||
channelName: String,
|
||||
rtmChannelListener: RtmChannelListener,
|
||||
rtmChannelJoinSuccess: () -> Unit,
|
||||
rtmChannelJoinFail: () -> Unit
|
||||
) {
|
||||
rtmChannel = rtmClient!!.createChannel(channelName, rtmChannelListener)
|
||||
rtmClient!!.login(
|
||||
rtmToken,
|
||||
uid,
|
||||
object : ResultCallback<Void> {
|
||||
override fun onSuccess(p0: Void?) {
|
||||
rtmChannel!!.join(object : ResultCallback<Void> {
|
||||
override fun onSuccess(p0: Void?) {
|
||||
Logger.e("rtmChannel join - onSuccess")
|
||||
rtmChannelJoinSuccess()
|
||||
}
|
||||
|
||||
override fun onFailure(p0: ErrorInfo?) {
|
||||
rtmChannelJoinFail()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onFailure(p0: ErrorInfo?) {
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun sendRawMessageToGroup(
|
||||
rawMessage: ByteArray,
|
||||
onSuccess: (() -> Unit)? = null,
|
||||
onFailure: (() -> Unit)? = null
|
||||
) {
|
||||
val message = rtmClient!!.createMessage()
|
||||
message.rawMessage = rawMessage
|
||||
rtmChannel!!.sendMessage(
|
||||
message,
|
||||
object : ResultCallback<Void?> {
|
||||
override fun onSuccess(p0: Void?) {
|
||||
Logger.e("sendMessage - onSuccess")
|
||||
onSuccess?.invoke()
|
||||
}
|
||||
|
||||
override fun onFailure(p0: ErrorInfo) {
|
||||
Logger.e("sendMessage fail - ${p0.errorCode}")
|
||||
Logger.e("sendMessage fail - ${p0.errorDescription}")
|
||||
onFailure?.invoke()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun setClientRole(role: Int) {
|
||||
rtcEngine!!.setClientRole(role)
|
||||
}
|
||||
|
||||
fun muteLocalAudioStream(muted: Boolean) {
|
||||
rtcEngine?.muteLocalAudioStream(muted)
|
||||
}
|
||||
|
||||
fun muteAllRemoteAudioStreams(mute: Boolean) {
|
||||
rtcEngine?.muteAllRemoteAudioStreams(mute)
|
||||
}
|
||||
|
||||
fun sendRawMessageToPeer(
|
||||
receiverUid: String,
|
||||
requestType: LiveRoomRequestType,
|
||||
onSuccess: () -> Unit
|
||||
) {
|
||||
val option = SendMessageOptions()
|
||||
|
||||
val message = rtmClient!!.createMessage()
|
||||
message.rawMessage = requestType.toString().toByteArray()
|
||||
|
||||
rtmClient!!.sendMessageToPeer(
|
||||
receiverUid,
|
||||
message,
|
||||
option,
|
||||
object : ResultCallback<Void?> {
|
||||
override fun onSuccess(aVoid: Void?) {
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
override fun onFailure(errorInfo: ErrorInfo) {
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun rtmChannelIsNull(): Boolean {
|
||||
return rtmChannel == null
|
||||
}
|
||||
|
||||
fun getConnectionState(): Int {
|
||||
return rtcEngine!!.connectionState
|
||||
}
|
||||
}
|
|
@ -25,4 +25,6 @@ object Constants {
|
|||
const val EXTRA_LIVE_RESERVATION_RESPONSE = "extra_live_reservation_response"
|
||||
|
||||
const val EXTRA_CONTENT_ID = "extra_content_id"
|
||||
|
||||
const val LIVE_SERVICE_NOTIFICATION_ID: Int = 2
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
package kr.co.vividnext.sodalive.common
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import androidx.core.app.NotificationCompat
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.live.LiveViewModel
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomActivity
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class SodaLiveService : Service() {
|
||||
|
||||
private val liveViewModel: LiveViewModel by inject()
|
||||
|
||||
var roomId: Long = 0
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val content = intent?.getStringExtra("content") ?: "라이브 진행중"
|
||||
roomId = intent?.getLongExtra("roomId", 0) ?: 0L
|
||||
updateNotification(content)
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
private fun updateNotification(content: String) {
|
||||
startForeground(Constants.LIVE_SERVICE_NOTIFICATION_ID, createNotification(content))
|
||||
}
|
||||
|
||||
private fun createNotification(content: String): Notification {
|
||||
val notificationChannelId = "soda_live_service_foreground_service_channel"
|
||||
val notificationManager =
|
||||
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel =
|
||||
NotificationChannel(
|
||||
notificationChannelId,
|
||||
getString(R.string.app_name),
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
)
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
val intent = Intent(this, LiveRoomActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
|
||||
)
|
||||
|
||||
val notificationBuilder = NotificationCompat.Builder(this, notificationChannelId)
|
||||
.setSmallIcon(R.drawable.ic_noti)
|
||||
.setContentTitle(getString(R.string.app_name))
|
||||
.setContentText(content)
|
||||
.setOngoing(true)
|
||||
.setSilent(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
|
||||
return notificationBuilder.build()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
liveViewModel.quitRoom(roomId) { }
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun stopService(context: Context) {
|
||||
val intent = Intent(context, SodaLiveService::class.java)
|
||||
context.stopService(intent)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,8 +9,10 @@ import kr.co.vividnext.sodalive.live.LiveRepository
|
|||
import kr.co.vividnext.sodalive.live.LiveViewModel
|
||||
import kr.co.vividnext.sodalive.live.recommend.LiveRecommendApi
|
||||
import kr.co.vividnext.sodalive.live.recommend.LiveRecommendRepository
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomViewModel
|
||||
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel
|
||||
import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailViewModel
|
||||
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessageViewModel
|
||||
import kr.co.vividnext.sodalive.live.room.tag.LiveTagRepository
|
||||
import kr.co.vividnext.sodalive.live.room.tag.LiveTagViewModel
|
||||
import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditViewModel
|
||||
|
@ -24,6 +26,8 @@ import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeViewModel
|
|||
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusViewModel
|
||||
import kr.co.vividnext.sodalive.network.TokenAuthenticator
|
||||
import kr.co.vividnext.sodalive.report.ReportApi
|
||||
import kr.co.vividnext.sodalive.report.ReportRepository
|
||||
import kr.co.vividnext.sodalive.settings.event.EventApi
|
||||
import kr.co.vividnext.sodalive.settings.event.EventRepository
|
||||
import kr.co.vividnext.sodalive.settings.terms.TermsApi
|
||||
|
@ -76,13 +80,14 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||
.build()
|
||||
}
|
||||
|
||||
single { ApiBuilder().build(get(), UserApi::class.java) }
|
||||
single { ApiBuilder().build(get(), TermsApi::class.java) }
|
||||
single { ApiBuilder().build(get(), LiveApi::class.java) }
|
||||
single { ApiBuilder().build(get(), EventApi::class.java) }
|
||||
single { ApiBuilder().build(get(), LiveRecommendApi::class.java) }
|
||||
single { ApiBuilder().build(get(), AuthApi::class.java) }
|
||||
single { ApiBuilder().build(get(), CanApi::class.java) }
|
||||
single { ApiBuilder().build(get(), AuthApi::class.java) }
|
||||
single { ApiBuilder().build(get(), UserApi::class.java) }
|
||||
single { ApiBuilder().build(get(), LiveApi::class.java) }
|
||||
single { ApiBuilder().build(get(), TermsApi::class.java) }
|
||||
single { ApiBuilder().build(get(), EventApi::class.java) }
|
||||
single { ApiBuilder().build(get(), ReportApi::class.java) }
|
||||
single { ApiBuilder().build(get(), LiveRecommendApi::class.java) }
|
||||
}
|
||||
|
||||
private val viewModelModule = module {
|
||||
|
@ -100,17 +105,20 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||
viewModel { LiveRoomCreateViewModel(get()) }
|
||||
viewModel { LiveTagViewModel(get()) }
|
||||
viewModel { LiveRoomEditViewModel(get()) }
|
||||
viewModel { LiveRoomViewModel(get(), get(), get()) }
|
||||
viewModel { LiveRoomDonationMessageViewModel(get()) }
|
||||
}
|
||||
|
||||
private val repositoryModule = module {
|
||||
factory { UserRepository(get()) }
|
||||
factory { TermsRepository(get()) }
|
||||
factory { LiveRepository(get()) }
|
||||
factory { LiveRepository(get(), get()) }
|
||||
factory { EventRepository(get()) }
|
||||
factory { LiveRecommendRepository(get()) }
|
||||
factory { AuthRepository(get()) }
|
||||
factory { CanRepository(get()) }
|
||||
factory { LiveTagRepository(get()) }
|
||||
factory { ReportRepository(get()) }
|
||||
}
|
||||
|
||||
private val moduleList = listOf(
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package kr.co.vividnext.sodalive.explorer.profile
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class MemberBlockRequest(@SerializedName("blockMemberId") val blockMemberId: Long)
|
|
@ -8,15 +8,25 @@ import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationResponse
|
|||
import kr.co.vividnext.sodalive.live.room.CancelLiveRequest
|
||||
import kr.co.vividnext.sodalive.live.room.EnterOrQuitLiveRoomRequest
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomStatus
|
||||
import kr.co.vividnext.sodalive.live.room.SetManagerOrSpeakerOrAudienceRequest
|
||||
import kr.co.vividnext.sodalive.live.room.StartLiveRequest
|
||||
import kr.co.vividnext.sodalive.live.room.create.CreateLiveRoomResponse
|
||||
import kr.co.vividnext.sodalive.live.room.create.GetRecentRoomInfoResponse
|
||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
|
||||
import kr.co.vividnext.sodalive.live.room.donation.DeleteLiveRoomDonationMessage
|
||||
import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationStatusResponse
|
||||
import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationTotalResponse
|
||||
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessage
|
||||
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationRequest
|
||||
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.profile.GetLiveRoomUserProfileResponse
|
||||
import kr.co.vividnext.sodalive.live.room.tag.GetLiveTagResponse
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.HTTP
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
|
@ -93,4 +103,83 @@ interface LiveApi {
|
|||
@Part("request") request: RequestBody?,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@GET("/live/room/info/{id}")
|
||||
fun getRoomInfo(
|
||||
@Path("id") id: Long,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<GetRoomInfoResponse>>
|
||||
|
||||
@GET("/live/room/donation-message")
|
||||
fun getDonationMessageList(
|
||||
@Query("roomId") roomId: Long,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<LiveRoomDonationMessage>>>
|
||||
|
||||
@HTTP(method = "DELETE", path = "/live/room/donation-message", hasBody = true)
|
||||
fun deleteDonationMessage(
|
||||
@Body request: DeleteLiveRoomDonationMessage,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@GET("/live/room/{room_id}/profile/{user_id}")
|
||||
fun getUserProfile(
|
||||
@Path("room_id") roomId: Long,
|
||||
@Path("user_id") userId: Long,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<GetLiveRoomUserProfileResponse>>
|
||||
|
||||
@GET("/live/room/{id}/donation-total")
|
||||
fun getDonationTotal(
|
||||
@Path("id") id: Long,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<GetLiveRoomDonationTotalResponse>>
|
||||
|
||||
@PUT("/live/room/info/set/speaker")
|
||||
fun setSpeaker(
|
||||
@Body request: SetManagerOrSpeakerOrAudienceRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@PUT("/live/room/info/set/listener")
|
||||
fun setListener(
|
||||
@Body request: SetManagerOrSpeakerOrAudienceRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@POST("/live/room/kick-out")
|
||||
fun kickOut(
|
||||
@Body request: LiveRoomKickOutRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@POST("/live/room/donation")
|
||||
fun donation(
|
||||
@Body request: LiveRoomDonationRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@POST("/live/room/donation/refund/{id}")
|
||||
fun refundDonation(
|
||||
@Path("id") id: Long,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@POST("/live/room/quit")
|
||||
fun quitRoom(
|
||||
@Query("id") roomId: Long,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@PUT("/live/room/info/set/manager")
|
||||
fun setManager(
|
||||
@Body request: SetManagerOrSpeakerOrAudienceRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@GET("/live/room/{id}/donation-list")
|
||||
fun donationStatus(
|
||||
@Path("id") id: Long,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<GetLiveRoomDonationStatusResponse>>
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import kr.co.vividnext.sodalive.live.recommend.RecommendLiveAdapter
|
|||
import kr.co.vividnext.sodalive.live.recommend_channel.LiveRecommendChannelAdapter
|
||||
import kr.co.vividnext.sodalive.live.reservation.LiveReservationAdapter
|
||||
import kr.co.vividnext.sodalive.live.reservation.complete.LiveReservationCompleteActivity
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomActivity
|
||||
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateActivity
|
||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
|
||||
import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailFragment
|
||||
|
@ -65,7 +66,13 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
|
|||
ActivityResultContracts.StartActivityForResult()
|
||||
) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
val roomId = it.data?.getLongExtra(Constants.EXTRA_ROOM_ID, 0)
|
||||
val channelName = it.data?.getStringExtra(Constants.EXTRA_ROOM_CHANNEL_NAME)
|
||||
refreshSummary()
|
||||
|
||||
if (channelName != null) {
|
||||
enterLiveRoom(roomId = roomId!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -271,7 +278,7 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
|
|||
liveNowAdapter = LiveNowAdapter {
|
||||
val detailFragment = LiveRoomDetailFragment(
|
||||
it.roomId,
|
||||
onClickParticipant = {},
|
||||
onClickParticipant = { enterLiveRoom(it.roomId) },
|
||||
onClickReservation = {},
|
||||
onClickModify = {},
|
||||
onClickStart = {},
|
||||
|
@ -326,7 +333,10 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
|
|||
recyclerView.visibility = View.GONE
|
||||
binding.layoutLiveNow.tvAllView.visibility = View.GONE
|
||||
binding.layoutLiveNow.llNoItems.visibility = View.VISIBLE
|
||||
binding.layoutLiveNow.tvMakeRoom.setOnClickListener {}
|
||||
binding.layoutLiveNow.tvMakeRoom.setOnClickListener {
|
||||
val intent = Intent(requireContext(), LiveRoomCreateActivity::class.java)
|
||||
activityResultLauncher.launch(intent)
|
||||
}
|
||||
|
||||
recyclerView.requestLayout()
|
||||
binding.layoutLiveNow.llNoItems.requestLayout()
|
||||
|
@ -410,7 +420,11 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
|
|||
recyclerView.visibility = View.GONE
|
||||
binding.layoutLiveReservation.tvAllView.visibility = View.GONE
|
||||
binding.layoutLiveReservation.llNoItems.visibility = View.VISIBLE
|
||||
binding.layoutLiveReservation.tvMakeRoom.setOnClickListener {}
|
||||
binding.layoutLiveReservation.tvMakeRoom.setOnClickListener {
|
||||
val intent = Intent(requireContext(), LiveRoomCreateActivity::class.java)
|
||||
intent.putExtra(Constants.EXTRA_LIVE_TIME_NOW, false)
|
||||
activityResultLauncher.launch(intent)
|
||||
}
|
||||
|
||||
recyclerView.requestLayout()
|
||||
binding.layoutLiveReservation.llNoItems.requestLayout()
|
||||
|
@ -468,6 +482,11 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
|
|||
private fun startLive(roomId: Long) {
|
||||
val onEnterRoomSuccess = {
|
||||
viewModel.getSummary()
|
||||
requireActivity().runOnUiThread {
|
||||
val intent = Intent(requireContext(), LiveRoomActivity::class.java)
|
||||
intent.putExtra(Constants.EXTRA_ROOM_ID, roomId)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.startLive(roomId, onEnterRoomSuccess)
|
||||
|
@ -553,4 +572,90 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
|
|||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun enterLiveRoom(roomId: Long) {
|
||||
val onEnterRoomSuccess = {
|
||||
requireActivity().runOnUiThread {
|
||||
val intent = Intent(requireContext(), LiveRoomActivity::class.java)
|
||||
intent.putExtra(Constants.EXTRA_ROOM_ID, roomId)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.getRoomDetail(roomId) {
|
||||
if (it.channelName != null) {
|
||||
if (it.manager.id == SharedPreferenceManager.userId) {
|
||||
handler.postDelayed({
|
||||
viewModel.enterRoom(roomId, onEnterRoomSuccess)
|
||||
}, 300)
|
||||
} else if (it.price == 0 || it.isPaid) {
|
||||
if (it.isPrivateRoom) {
|
||||
LiveRoomPasswordDialog(
|
||||
activity = requireActivity(),
|
||||
layoutInflater = layoutInflater,
|
||||
can = 0,
|
||||
confirmButtonClick = { password ->
|
||||
viewModel.enterRoom(
|
||||
roomId = roomId,
|
||||
onSuccess = onEnterRoomSuccess,
|
||||
password = password
|
||||
)
|
||||
}
|
||||
).show(screenWidth)
|
||||
} else {
|
||||
handler.postDelayed({
|
||||
viewModel.enterRoom(roomId, onEnterRoomSuccess)
|
||||
}, 300)
|
||||
}
|
||||
} else {
|
||||
if (it.isPrivateRoom) {
|
||||
LiveRoomPasswordDialog(
|
||||
activity = requireActivity(),
|
||||
layoutInflater = layoutInflater,
|
||||
can = it.price,
|
||||
confirmButtonClick = { password ->
|
||||
handler.postDelayed({
|
||||
viewModel.enterRoom(
|
||||
roomId = roomId,
|
||||
onSuccess = onEnterRoomSuccess,
|
||||
password = password
|
||||
)
|
||||
}, 300)
|
||||
}
|
||||
).show(screenWidth)
|
||||
} else {
|
||||
LivePaymentDialog(
|
||||
activity = requireActivity(),
|
||||
layoutInflater = layoutInflater,
|
||||
title = "${it.price.moneyFormat()}코인으로 입장",
|
||||
desc = "'${it.title}' 라이브에 참여하기 위해 결제합니다.",
|
||||
confirmButtonTitle = "결제 후 입장",
|
||||
confirmButtonClick = {
|
||||
handler.postDelayed({
|
||||
viewModel.enterRoom(roomId, onEnterRoomSuccess)
|
||||
}, 300)
|
||||
},
|
||||
cancelButtonTitle = "취소",
|
||||
cancelButtonClick = {}
|
||||
).show(screenWidth)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val detailFragment = LiveRoomDetailFragment(
|
||||
it.roomId,
|
||||
onClickParticipant = {},
|
||||
onClickReservation = { reservationRoom(it.roomId) },
|
||||
onClickModify = { roomDetailResponse -> modifyLive(roomDetailResponse) },
|
||||
onClickStart = { startLive(it.roomId) },
|
||||
onClickCancel = { cancelLive(it.roomId) }
|
||||
)
|
||||
if (detailFragment.isAdded) return@getRoomDetail
|
||||
|
||||
detailFragment.show(
|
||||
requireActivity().supportFragmentManager,
|
||||
detailFragment.tag
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,23 @@ import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationRequest
|
|||
import kr.co.vividnext.sodalive.live.room.CancelLiveRequest
|
||||
import kr.co.vividnext.sodalive.live.room.EnterOrQuitLiveRoomRequest
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomStatus
|
||||
import kr.co.vividnext.sodalive.live.room.SetManagerOrSpeakerOrAudienceRequest
|
||||
import kr.co.vividnext.sodalive.live.room.StartLiveRequest
|
||||
import kr.co.vividnext.sodalive.live.room.create.CreateLiveRoomResponse
|
||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
|
||||
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.kick_out.LiveRoomKickOutRequest
|
||||
import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest
|
||||
import kr.co.vividnext.sodalive.user.UserApi
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import java.util.TimeZone
|
||||
|
||||
class LiveRepository(private val api: LiveApi) {
|
||||
class LiveRepository(
|
||||
private val api: LiveApi,
|
||||
private val userApi: UserApi
|
||||
) {
|
||||
fun roomList(
|
||||
dateString: String? = null,
|
||||
status: LiveRoomStatus,
|
||||
|
@ -94,4 +103,109 @@ class LiveRepository(private val api: LiveApi) {
|
|||
request = request,
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
fun getRoomInfo(roomId: Long, token: String) = api.getRoomInfo(roomId, authHeader = token)
|
||||
|
||||
fun getDonationMessageList(
|
||||
roomId: Long,
|
||||
token: String
|
||||
) = api.getDonationMessageList(roomId = roomId, authHeader = token)
|
||||
|
||||
fun deleteDonationMessage(
|
||||
roomId: Long,
|
||||
uuid: String,
|
||||
token: String
|
||||
) = api.deleteDonationMessage(
|
||||
request = DeleteLiveRoomDonationMessage(
|
||||
roomId,
|
||||
messageUUID = uuid
|
||||
),
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
fun getUserProfile(roomId: Long, userId: Long, token: String) = api.getUserProfile(
|
||||
roomId = roomId,
|
||||
userId = userId,
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
fun getTotalDonationCan(
|
||||
roomId: Long,
|
||||
token: String
|
||||
) = api.getDonationTotal(roomId, authHeader = token)
|
||||
|
||||
fun setSpeaker(roomId: Long, userId: Long, token: String): Single<ApiResponse<Any>> {
|
||||
return api.setSpeaker(
|
||||
request = SetManagerOrSpeakerOrAudienceRequest(roomId, accountId = userId),
|
||||
authHeader = token
|
||||
)
|
||||
}
|
||||
|
||||
fun setListener(roomId: Long, userId: Long, token: String): Single<ApiResponse<Any>> {
|
||||
return api.setListener(
|
||||
request = SetManagerOrSpeakerOrAudienceRequest(roomId, accountId = userId),
|
||||
authHeader = token
|
||||
)
|
||||
}
|
||||
|
||||
fun kickOut(roomId: Long, userId: Long, token: String): Single<ApiResponse<Any>> {
|
||||
return api.kickOut(
|
||||
request = LiveRoomKickOutRequest(roomId, userId),
|
||||
authHeader = token
|
||||
)
|
||||
}
|
||||
|
||||
fun donation(
|
||||
roomId: Long,
|
||||
can: Int,
|
||||
message: String,
|
||||
token: String
|
||||
): Single<ApiResponse<Any>> {
|
||||
return api.donation(
|
||||
request = LiveRoomDonationRequest(
|
||||
roomId = roomId,
|
||||
can = can,
|
||||
message = message,
|
||||
container = "aos"
|
||||
),
|
||||
authHeader = token
|
||||
)
|
||||
}
|
||||
|
||||
fun refundDonation(roomId: Long, token: String): Single<ApiResponse<Any>> {
|
||||
return api.refundDonation(
|
||||
id = roomId,
|
||||
authHeader = token
|
||||
)
|
||||
}
|
||||
|
||||
fun quitRoom(roomId: Long, token: String): Single<ApiResponse<Any>> {
|
||||
return api.quitRoom(roomId = roomId, authHeader = token)
|
||||
}
|
||||
|
||||
fun setManager(roomId: Long, userId: Long, token: String) = api.setManager(
|
||||
request = SetManagerOrSpeakerOrAudienceRequest(roomId, accountId = userId),
|
||||
authHeader = token,
|
||||
)
|
||||
|
||||
fun creatorFollow(
|
||||
creatorId: Long,
|
||||
token: String
|
||||
) = userApi.creatorFollow(
|
||||
request = CreatorFollowRequestRequest(creatorId = creatorId),
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
fun creatorUnFollow(
|
||||
creatorId: Long,
|
||||
token: String
|
||||
) = userApi.creatorUnFollow(
|
||||
request = CreatorFollowRequestRequest(creatorId = creatorId),
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
fun donationStatus(
|
||||
roomId: Long,
|
||||
token: String
|
||||
) = api.donationStatus(roomId, authHeader = token)
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@ import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
|||
import kr.co.vividnext.sodalive.live.recommend.GetRecommendLiveResponse
|
||||
import kr.co.vividnext.sodalive.live.recommend.LiveRecommendRepository
|
||||
import kr.co.vividnext.sodalive.live.recommend_channel.GetRecommendChannelResponse
|
||||
import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationRequest
|
||||
import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationResponse
|
||||
import kr.co.vividnext.sodalive.live.room.CancelLiveRequest
|
||||
import kr.co.vividnext.sodalive.live.room.EnterOrQuitLiveRoomRequest
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomStatus
|
||||
import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationRequest
|
||||
import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationResponse
|
||||
import kr.co.vividnext.sodalive.live.room.StartLiveRequest
|
||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
|
||||
import kr.co.vividnext.sodalive.settings.event.EventItem
|
||||
|
@ -245,6 +245,37 @@ class LiveViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun quitRoom(roomId: Long, onSuccess: () -> Unit) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.quitRoom(roomId, "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success) {
|
||||
onSuccess()
|
||||
_isLoading.value = false
|
||||
} else {
|
||||
_isLoading.value = false
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun startLive(roomId: Long, onEnterRoomSuccess: () -> Unit) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
|
@ -313,7 +344,7 @@ class LiveViewModel(
|
|||
)
|
||||
}
|
||||
|
||||
fun enterRoom(roomId: Long, onSuccess: () -> Unit, password: Int? = null) {
|
||||
fun enterRoom(roomId: Long, onSuccess: () -> Unit, password: String? = null) {
|
||||
_isLoading.value = true
|
||||
val request = EnterOrQuitLiveRoomRequest(roomId, password = password)
|
||||
compositeDisposable.add(
|
||||
|
|
|
@ -5,5 +5,5 @@ import com.google.gson.annotations.SerializedName
|
|||
data class EnterOrQuitLiveRoomRequest(
|
||||
@SerializedName("roomId") val roomId: Long,
|
||||
@SerializedName("container") val container: String = "aos",
|
||||
@SerializedName("password") val password: Int? = null
|
||||
@SerializedName("password") val password: String? = null
|
||||
)
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package kr.co.vividnext.sodalive.live.room
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
|
||||
class LiveRoomDialog(
|
||||
activity: Activity,
|
||||
layoutInflater: LayoutInflater,
|
||||
) {
|
||||
|
||||
private val alertDialog: AlertDialog
|
||||
private val dialogView = DialogLiveRoomBinding.inflate(layoutInflater)
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
init {
|
||||
val dialogBuilder = AlertDialog.Builder(activity)
|
||||
dialogBuilder.setView(dialogView.root)
|
||||
|
||||
alertDialog = dialogBuilder.create()
|
||||
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
}
|
||||
|
||||
fun show(width: Int) {
|
||||
if (!alertDialog.isShowing) {
|
||||
alertDialog.show()
|
||||
|
||||
val lp = WindowManager.LayoutParams()
|
||||
lp.copyFrom(alertDialog.window?.attributes)
|
||||
lp.width = width - (53.4f.dpToPx()).toInt()
|
||||
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
|
||||
alertDialog.window?.attributes = lp
|
||||
|
||||
if (dialogView.llActionButtons.visibility == View.GONE) {
|
||||
alertDialog.setCancelable(true)
|
||||
val llContentLp = dialogView.llContent.layoutParams as LinearLayout.LayoutParams
|
||||
llContentLp.bottomMargin = 0f.dpToPx().toInt()
|
||||
dialogView.llContent.layoutParams = llContentLp
|
||||
|
||||
handler.postDelayed({
|
||||
dismiss()
|
||||
}, 1000)
|
||||
} else {
|
||||
alertDialog.setCancelable(false)
|
||||
val llContentLp = dialogView.llContent.layoutParams as LinearLayout.LayoutParams
|
||||
llContentLp.bottomMargin = 10f.dpToPx().toInt()
|
||||
dialogView.llContent.layoutParams = llContentLp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun dismiss() {
|
||||
alertDialog.dismiss()
|
||||
dialogView.tvContent.text = ""
|
||||
dialogView.llActionButtons.visibility = View.GONE
|
||||
dialogView.tvCancel.text = ""
|
||||
dialogView.tvConfirm.text = ""
|
||||
dialogView.tvCancel.setOnClickListener { }
|
||||
dialogView.tvConfirm.setOnClickListener { }
|
||||
|
||||
val llContentLp = dialogView.llContent.layoutParams as LinearLayout.LayoutParams
|
||||
llContentLp.bottomMargin = 10f.dpToPx().toInt()
|
||||
dialogView.llContent.layoutParams = llContentLp
|
||||
}
|
||||
|
||||
fun setCancel(title: String, action: () -> Unit) {
|
||||
dialogView.tvCancel.text = title
|
||||
dialogView.tvCancel.setOnClickListener {
|
||||
dismiss()
|
||||
action()
|
||||
}
|
||||
|
||||
dialogView.llActionButtons.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
fun setConfirm(title: String, action: () -> Unit) {
|
||||
dialogView.tvConfirm.text = title
|
||||
dialogView.tvConfirm.setOnClickListener {
|
||||
dismiss()
|
||||
action()
|
||||
}
|
||||
|
||||
dialogView.llActionButtons.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
fun setContent(content: String) {
|
||||
dialogView.tvContent.text = content
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package kr.co.vividnext.sodalive.live.room
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
enum class LiveRoomRequestType {
|
||||
@SerializedName("INVITE_SPEAKER") INVITE_SPEAKER,
|
||||
@SerializedName("CHANGE_LISTENER") CHANGE_LISTENER,
|
||||
@SerializedName("KICK_OUT") KICK_OUT,
|
||||
@SerializedName("SET_MANAGER") SET_MANAGER,
|
||||
@SerializedName("RELEASE_MANAGER") RELEASE_MANAGER,
|
||||
}
|
|
@ -0,0 +1,778 @@
|
|||
package kr.co.vividnext.sodalive.live.room
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.google.firebase.dynamiclinks.ShortDynamicLink
|
||||
import com.google.firebase.dynamiclinks.ktx.androidParameters
|
||||
import com.google.firebase.dynamiclinks.ktx.dynamicLinks
|
||||
import com.google.firebase.dynamiclinks.ktx.iosParameters
|
||||
import com.google.firebase.dynamiclinks.ktx.shortLinkAsync
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import com.google.gson.Gson
|
||||
import com.orhanobut.logger.Logger
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.live.LiveRepository
|
||||
import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationStatusResponse
|
||||
import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse
|
||||
import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse
|
||||
import kr.co.vividnext.sodalive.live.room.update.EditLiveRoomInfoRequest
|
||||
import kr.co.vividnext.sodalive.report.ReportRepository
|
||||
import kr.co.vividnext.sodalive.report.ReportRequest
|
||||
import kr.co.vividnext.sodalive.report.ReportType
|
||||
import kr.co.vividnext.sodalive.user.UserRepository
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.io.File
|
||||
|
||||
class LiveRoomViewModel(
|
||||
private val repository: LiveRepository,
|
||||
private val userRepository: UserRepository,
|
||||
private val reportRepository: ReportRepository
|
||||
) : BaseViewModel() {
|
||||
private val _roomInfoLiveData = MutableLiveData<GetRoomInfoResponse>()
|
||||
val roomInfoLiveData: LiveData<GetRoomInfoResponse>
|
||||
get() = _roomInfoLiveData
|
||||
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
private val _isShowNotice = MutableLiveData(true)
|
||||
val isShowNotice: LiveData<Boolean>
|
||||
get() = _isShowNotice
|
||||
|
||||
private val _isExpandNotice = MutableLiveData(false)
|
||||
val isExpandNotice: LiveData<Boolean>
|
||||
get() = _isExpandNotice
|
||||
|
||||
private val _totalDonationCan = MutableLiveData(0)
|
||||
val totalDonationCan: LiveData<Int>
|
||||
get() = _totalDonationCan
|
||||
|
||||
private val _userProfileLiveData = MutableLiveData<GetLiveRoomUserProfileResponse>()
|
||||
val userProfileLiveData: LiveData<GetLiveRoomUserProfileResponse>
|
||||
get() = _userProfileLiveData
|
||||
|
||||
lateinit var roomInfoResponse: GetRoomInfoResponse
|
||||
|
||||
fun isRoomInfoInitialized() = this::roomInfoResponse.isInitialized
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
private var _isBgOn = MutableLiveData(true)
|
||||
val isBgOn: LiveData<Boolean>
|
||||
get() = _isBgOn
|
||||
|
||||
lateinit var getRealPathFromURI: (Uri) -> String?
|
||||
|
||||
fun getUserNickname(memberId: Int): String {
|
||||
for (manager in roomInfoResponse.managerList) {
|
||||
if (manager.id.toInt() == memberId) {
|
||||
return manager.nickname
|
||||
}
|
||||
}
|
||||
|
||||
for (speaker in roomInfoResponse.speakerList) {
|
||||
if (speaker.id.toInt() == memberId) {
|
||||
return speaker.nickname
|
||||
}
|
||||
}
|
||||
|
||||
for (listener in roomInfoResponse.listenerList) {
|
||||
if (listener.id.toInt() == memberId) {
|
||||
return listener.nickname
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
fun getUserProfileUrl(accountId: Int): String {
|
||||
for (manager in roomInfoResponse.managerList) {
|
||||
if (manager.id.toInt() == accountId) {
|
||||
return manager.profileImage
|
||||
}
|
||||
}
|
||||
|
||||
for (speaker in roomInfoResponse.speakerList) {
|
||||
if (speaker.id.toInt() == accountId) {
|
||||
return speaker.profileImage
|
||||
}
|
||||
}
|
||||
|
||||
for (listener in roomInfoResponse.listenerList) {
|
||||
if (listener.id.toInt() == accountId) {
|
||||
return listener.profileImage
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
fun getManagerNickname(): String {
|
||||
return roomInfoResponse.managerNickname
|
||||
}
|
||||
|
||||
fun setSpeaker(roomId: Long, userId: Long, onSuccess: () -> Unit) {
|
||||
compositeDisposable.add(
|
||||
repository.setSpeaker(roomId, userId, "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success) {
|
||||
onSuccess()
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun setListener(roomId: Long, userId: Long, onSuccess: () -> Unit) {
|
||||
compositeDisposable.add(
|
||||
repository.setListener(roomId, userId, "Bearer ${SharedPreferenceManager.token}")
|
||||
.retry(3)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success) {
|
||||
onSuccess()
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun getRoomInfo(roomId: Long, userId: Int = 0, onSuccess: (String) -> Unit = {}) {
|
||||
compositeDisposable.add(
|
||||
repository.getRoomInfo(roomId, "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success && it.data != null) {
|
||||
roomInfoResponse = it.data
|
||||
Logger.e("data: ${it.data}")
|
||||
_roomInfoLiveData.postValue(roomInfoResponse)
|
||||
|
||||
getTotalDonationCan(roomId = roomId)
|
||||
|
||||
if (userId > 0) {
|
||||
val nickname = getUserNickname(userId)
|
||||
onSuccess(nickname)
|
||||
}
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun isEqualToHostId(accountId: Int): Boolean {
|
||||
return accountId == roomInfoResponse.managerId.toInt()
|
||||
}
|
||||
|
||||
fun getMemberCan() {
|
||||
compositeDisposable.add(
|
||||
userRepository.getMemberInfo(token = "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success && it.data != null) {
|
||||
SharedPreferenceManager.can = it.data.can
|
||||
}
|
||||
},
|
||||
{
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun shareRoomLink(
|
||||
roomId: Long,
|
||||
isPrivateRoom: Boolean,
|
||||
password: String?,
|
||||
onSuccess: (String) -> Unit
|
||||
) {
|
||||
_isLoading.value = true
|
||||
Firebase.dynamicLinks.shortLinkAsync(ShortDynamicLink.Suffix.SHORT) {
|
||||
link = Uri.parse("https://yozm.day/?room_id=$roomId")
|
||||
domainUriPrefix = "https://yozm.page.link"
|
||||
androidParameters { }
|
||||
iosParameters("kr.co.vividnext.yozm") {
|
||||
appStoreId = "1630284226"
|
||||
}
|
||||
}.addOnSuccessListener {
|
||||
val uri = it.shortLink
|
||||
if (uri != null) {
|
||||
val message = if (isPrivateRoom) {
|
||||
"${SharedPreferenceManager.nickname}님이 귀하를 " +
|
||||
"소다라이브의 비공개라이브에 초대하였습니다.\n" +
|
||||
"※ 라이브 참여: $uri\n" +
|
||||
"(입장 비밀번호 : $password)"
|
||||
} else {
|
||||
"${SharedPreferenceManager.nickname}님이 귀하를 " +
|
||||
"소다라이브의 공개라이브에 초대하였습니다.\n" +
|
||||
"※ 라이브 참여: $uri"
|
||||
}
|
||||
|
||||
onSuccess(message)
|
||||
}
|
||||
}.addOnFailureListener {
|
||||
_toastLiveData.postValue("공유링크를 생성하지 못했습니다.\n다시 시도해 주세요.")
|
||||
}.addOnCompleteListener {
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
fun creatorFollow(creatorId: Long, roomId: Long, isGetUserProfile: Boolean = false) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.creatorFollow(
|
||||
creatorId,
|
||||
"Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success && it.data != null) {
|
||||
getRoomInfo(roomId)
|
||||
|
||||
if (isGetUserProfile) {
|
||||
getUserProfile(roomId = roomId, userId = creatorId) {}
|
||||
}
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
_isLoading.value = false
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun creatorUnFollow(creatorId: Long, roomId: Long, isGetUserProfile: Boolean = false) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.creatorUnFollow(
|
||||
creatorId,
|
||||
"Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success && it.data != null) {
|
||||
getRoomInfo(roomId)
|
||||
|
||||
if (isGetUserProfile) {
|
||||
getUserProfile(roomId = roomId, userId = creatorId) {}
|
||||
}
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
_isLoading.value = false
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun toggleShowNotice() {
|
||||
_isShowNotice.value = !isShowNotice.value!!
|
||||
_isExpandNotice.value = false
|
||||
}
|
||||
|
||||
fun toggleExpandNotice() {
|
||||
_isExpandNotice.value = !isExpandNotice.value!!
|
||||
}
|
||||
|
||||
fun toggleBackgroundImage() {
|
||||
_isBgOn.value = !isBgOn.value!!
|
||||
}
|
||||
|
||||
fun editLiveRoomInfo(
|
||||
roomId: Long,
|
||||
newTitle: String,
|
||||
newContent: String,
|
||||
newCoverImageUri: Uri? = null,
|
||||
onSuccess: () -> Unit
|
||||
) {
|
||||
val request = EditLiveRoomInfoRequest(
|
||||
title = if (newTitle != roomInfoResponse.title) {
|
||||
newTitle
|
||||
} else {
|
||||
null
|
||||
},
|
||||
notice = if (newContent != roomInfoResponse.notice) {
|
||||
newContent
|
||||
} else {
|
||||
null
|
||||
},
|
||||
numberOfPeople = null,
|
||||
beginDateTimeString = null,
|
||||
timezone = null
|
||||
)
|
||||
|
||||
val requestJson = if (request.title != null || request.notice != null) {
|
||||
Gson().toJson(request)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val coverImage = if (newCoverImageUri != null) {
|
||||
val file = File(getRealPathFromURI(newCoverImageUri!!))
|
||||
MultipartBody.Part.createFormData(
|
||||
"coverImage",
|
||||
file.name,
|
||||
file.asRequestBody("image/*".toMediaType())
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
if (coverImage == null && requestJson == null) {
|
||||
_toastLiveData.value = "변경사항이 없습니다."
|
||||
return
|
||||
}
|
||||
|
||||
compositeDisposable.add(
|
||||
repository.editLiveRoomInfo(
|
||||
roomId = roomId,
|
||||
coverImage = coverImage,
|
||||
request = requestJson?.toRequestBody("text/plain".toMediaType()),
|
||||
token = "Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success) {
|
||||
getRoomInfo(roomId = roomId)
|
||||
onSuccess()
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"라이브 정보를 수정하지 못했습니다.\n다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue(
|
||||
"라이브 정보를 수정하지 못했습니다.\n다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun quitRoom(roomId: Long, onSuccess: () -> Unit) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.quitRoom(roomId = roomId, token = "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success) {
|
||||
onSuccess()
|
||||
_isLoading.value = false
|
||||
} else {
|
||||
_isLoading.value = false
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun kickOut(roomId: Long, userId: Long) {
|
||||
compositeDisposable.add(
|
||||
repository.kickOut(roomId, userId, token = "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({}, {})
|
||||
)
|
||||
}
|
||||
|
||||
fun donation(roomId: Long, can: Int, message: String, onSuccess: () -> Unit) {
|
||||
_isLoading.postValue(true)
|
||||
compositeDisposable.add(
|
||||
repository.donation(roomId, can, message, "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success) {
|
||||
SharedPreferenceManager.can -= can
|
||||
onSuccess()
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun refundDonation(roomId: Long) {
|
||||
_isLoading.postValue(true)
|
||||
|
||||
compositeDisposable.add(
|
||||
repository.refundDonation(roomId, "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success) {
|
||||
_toastLiveData.postValue(
|
||||
"후원에 실패했습니다.\n다시 후원해주세요.\n" +
|
||||
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
)
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"후원에 실패한 코인이 환불되지 않았습니다\n고객센터로 문의해주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue(
|
||||
"후원에 실패한 코인이 환불되지 않았습니다\n고객센터로 문의해주세요."
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun addDonationCan(can: Int) {
|
||||
_totalDonationCan.postValue(totalDonationCan.value!! + can)
|
||||
}
|
||||
|
||||
fun donationStatus(roomId: Long, onSuccess: (GetLiveRoomDonationStatusResponse) -> Unit) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.donationStatus(roomId, token = "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success && it.data != null) {
|
||||
onSuccess(it.data)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"후원현황을 가져오지 못했습니다\n다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue(
|
||||
"후원현황을 가져오지 못했습니다\n다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun getUserRank(userId: Long): Int {
|
||||
// 방장 -> -2
|
||||
// 스탭 -> -3
|
||||
// 나머지 -> 체크
|
||||
return if (isEqualToHostId(userId.toInt())) {
|
||||
-2
|
||||
} else if (isEqualToManagerId(userId.toInt())) {
|
||||
-3
|
||||
} else {
|
||||
roomInfoResponse.donationRankingTop3UserIds.indexOf(userId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTotalDonationCan(roomId: Long) {
|
||||
compositeDisposable.add(
|
||||
repository.getTotalDonationCan(
|
||||
roomId = roomId,
|
||||
token = "Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success && it.data != null) {
|
||||
_totalDonationCan.postValue(it.data.totalDonationCan)
|
||||
}
|
||||
},
|
||||
{
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun getUserProfile(roomId: Long, userId: Long, onSuccess: () -> Unit) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.getUserProfile(
|
||||
roomId = roomId,
|
||||
userId = userId,
|
||||
token = "Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success && it.data != null) {
|
||||
_userProfileLiveData.value = it.data!!
|
||||
onSuccess()
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun setManager(roomId: Long, userId: Long, onSuccess: () -> Unit) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.setManager(roomId, userId, "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success) {
|
||||
getRoomInfo(roomId)
|
||||
onSuccess()
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun isEqualToManagerId(accountId: Int): Boolean {
|
||||
for (manager in roomInfoResponse.managerList) {
|
||||
if (manager.id == accountId.toLong()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun memberBlock(userId: Long, onSuccess: () -> Unit) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
userRepository.memberBlock(
|
||||
userId = userId,
|
||||
token = "Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
|
||||
if (it.success) {
|
||||
_toastLiveData.postValue("차단하였습니다.")
|
||||
onSuccess()
|
||||
} else {
|
||||
val message = it.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
_toastLiveData.postValue(message)
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun memberUnBlock(userId: Long) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
userRepository.memberUnBlock(
|
||||
userId = userId,
|
||||
token = "Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
|
||||
if (it.success) {
|
||||
getUserProfile(roomId = roomInfoResponse.roomId, userId = userId) {}
|
||||
_toastLiveData.postValue("차단이 해제 되었습니다.")
|
||||
} else {
|
||||
val message = it.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
_toastLiveData.postValue(message)
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun report(type: ReportType, userId: Long, reason: String = "프로필 신고") {
|
||||
_isLoading.value = true
|
||||
|
||||
val request = ReportRequest(type, reason, reportedAccountId = userId)
|
||||
compositeDisposable.add(
|
||||
reportRepository.report(
|
||||
request = request,
|
||||
token = "Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"신고가 접수되었습니다."
|
||||
)
|
||||
}
|
||||
|
||||
_isLoading.value = false
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("신고가 접수되었습니다.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package kr.co.vividnext.sodalive.live.room
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class SetManagerOrSpeakerOrAudienceRequest(
|
||||
@SerializedName("roomId") val roomId: Long,
|
||||
@SerializedName("accountId") val accountId: Long
|
||||
)
|
|
@ -0,0 +1,336 @@
|
|||
package kr.co.vividnext.sodalive.live.room.chat
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.text.SpannableString
|
||||
import android.text.Spanned
|
||||
import android.text.TextUtils
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.setPadding
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import coil.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.common.CustomTypefaceSpan
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomChatBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomDonationStatusChatBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomJoinChatBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationStatusResponse
|
||||
|
||||
enum class LiveRoomChatType {
|
||||
@SerializedName("CHAT")
|
||||
CHAT,
|
||||
|
||||
@SerializedName("DONATION_STATUS")
|
||||
DONATION_STATUS,
|
||||
|
||||
@SerializedName("JOIN")
|
||||
JOIN
|
||||
}
|
||||
|
||||
abstract class LiveRoomChat {
|
||||
open var type: LiveRoomChatType = LiveRoomChatType.CHAT
|
||||
abstract fun bind(
|
||||
context: Context,
|
||||
binding: ViewBinding,
|
||||
onClickProfile: ((Long) -> Unit)? = null
|
||||
)
|
||||
}
|
||||
|
||||
data class LiveRoomJoinChat(
|
||||
val nickname: String
|
||||
) : LiveRoomChat() {
|
||||
override var type = LiveRoomChatType.JOIN
|
||||
override fun bind(context: Context, binding: ViewBinding, onClickProfile: ((Long) -> Unit)?) {
|
||||
val str = "'$nickname'님이 입장하셨습니다."
|
||||
val spStr = SpannableString(str)
|
||||
|
||||
spStr.setSpan(
|
||||
ForegroundColorSpan(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.color_ffdc00
|
||||
)
|
||||
),
|
||||
str.indexOf("'") + 1,
|
||||
str.indexOf("'님"),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
|
||||
spStr.setSpan(
|
||||
CustomTypefaceSpan(
|
||||
ResourcesCompat.getFont(
|
||||
context,
|
||||
R.font.gmarket_sans_bold
|
||||
)
|
||||
),
|
||||
str.indexOf("'"),
|
||||
str.indexOf("'님"),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
|
||||
(binding as ItemLiveRoomJoinChatBinding).tvJoin.text = spStr
|
||||
}
|
||||
}
|
||||
|
||||
data class LiveRoomDonationStatusChat(
|
||||
val response: GetLiveRoomDonationStatusResponse? = null,
|
||||
val donationStatusString: String? = null
|
||||
) : LiveRoomChat() {
|
||||
override var type = LiveRoomChatType.DONATION_STATUS
|
||||
override fun bind(context: Context, binding: ViewBinding, onClickProfile: ((Long) -> Unit)?) {
|
||||
if (donationStatusString != null) {
|
||||
(binding as ItemLiveRoomDonationStatusChatBinding)
|
||||
.tvDonationList
|
||||
.text = donationStatusString
|
||||
} else {
|
||||
(binding as ItemLiveRoomDonationStatusChatBinding)
|
||||
.tvDonationList
|
||||
.text = getDonationString(context)
|
||||
}
|
||||
}
|
||||
|
||||
fun getDonationString(context: Context): String {
|
||||
if (response != null) {
|
||||
var donationStatusString: CharSequence = "[현재 라이브 후원현황]\n\n"
|
||||
for (index in response.donationList.indices) {
|
||||
val donation = response.donationList[index]
|
||||
val spChars = SpannableString(
|
||||
"${index + 1}. " +
|
||||
if (donation.nickname.length > 10) {
|
||||
"${donation.nickname.substring(0, 10)} : "
|
||||
} else {
|
||||
"${donation.nickname} : "
|
||||
} + "(${donation.can.moneyFormat()} 캔)\n"
|
||||
)
|
||||
spChars.setSpan(
|
||||
ForegroundColorSpan(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.color_fdca2f
|
||||
)
|
||||
),
|
||||
spChars.indexOf(": (") + 3,
|
||||
spChars.indexOf(")"),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
|
||||
donationStatusString = TextUtils.concat(
|
||||
donationStatusString, spChars
|
||||
)
|
||||
}
|
||||
|
||||
donationStatusString = TextUtils.concat(
|
||||
donationStatusString,
|
||||
"\n-------------------------\n\n"
|
||||
)
|
||||
|
||||
donationStatusString = TextUtils.concat(
|
||||
donationStatusString,
|
||||
"후원인원 : ${response.totalCount} 명\n"
|
||||
)
|
||||
|
||||
donationStatusString = TextUtils.concat(
|
||||
donationStatusString,
|
||||
"후원합계 : ${response.totalCan.moneyFormat()} 캔"
|
||||
)
|
||||
|
||||
return donationStatusString.toString()
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class LiveRoomNormalChat(
|
||||
@SerializedName("userId") val userId: Long,
|
||||
@SerializedName("profileUrl") val profileUrl: String,
|
||||
@SerializedName("nickname") val nickname: String,
|
||||
@SerializedName("rank") val rank: Int,
|
||||
@SerializedName("chat") val chat: String,
|
||||
) : LiveRoomChat() {
|
||||
override fun bind(context: Context, binding: ViewBinding, onClickProfile: ((Long) -> Unit)?) {
|
||||
val itemBinding = binding as ItemLiveRoomChatBinding
|
||||
itemBinding.ivProfile.load(profileUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(RoundedCornersTransformation(23.3f.dpToPx()))
|
||||
}
|
||||
|
||||
itemBinding.ivProfile.setOnClickListener {
|
||||
if (onClickProfile != null && userId != SharedPreferenceManager.userId) {
|
||||
onClickProfile(userId)
|
||||
}
|
||||
}
|
||||
|
||||
itemBinding.tvChat.text = chat
|
||||
itemBinding.tvNickname.text = nickname
|
||||
|
||||
itemBinding.ivBg.visibility = View.VISIBLE
|
||||
itemBinding.tvCreatorOrManager.visibility = View.GONE
|
||||
|
||||
when (rank + 1) {
|
||||
-2 -> {
|
||||
itemBinding.ivBg.setImageResource(R.drawable.bg_circle_4999e3)
|
||||
itemBinding.ivCrown.setImageResource(R.drawable.ic_badge_manager)
|
||||
itemBinding.tvCreatorOrManager.setBackgroundResource(
|
||||
R.drawable.bg_round_corner_2_4999e3
|
||||
)
|
||||
itemBinding.tvCreatorOrManager.text = "스탭"
|
||||
|
||||
itemBinding.ivCrown.visibility = View.VISIBLE
|
||||
itemBinding.tvCreatorOrManager.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
-1 -> {
|
||||
itemBinding.ivBg.setImageResource(R.drawable.bg_circle_6f3dec_9970ff)
|
||||
itemBinding.ivCrown.setImageResource(R.drawable.ic_crown)
|
||||
itemBinding.ivCrown.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
1 -> {
|
||||
itemBinding.ivBg.setImageResource(R.drawable.bg_circle_ffdc00_fdca2f)
|
||||
itemBinding.ivCrown.setImageResource(R.drawable.ic_crown_1)
|
||||
itemBinding.ivCrown.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
2 -> {
|
||||
itemBinding.ivBg.setImageResource(R.drawable.bg_circle_9f9f9f_dcdcdc)
|
||||
itemBinding.ivCrown.setImageResource(R.drawable.ic_crown_2)
|
||||
itemBinding.ivCrown.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
3 -> {
|
||||
itemBinding.ivBg.setImageResource(R.drawable.bg_circle_e5a578_c67e4a)
|
||||
itemBinding.ivCrown.setImageResource(R.drawable.ic_crown_3)
|
||||
itemBinding.ivCrown.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
else -> {
|
||||
itemBinding.ivBg.setImageResource(R.drawable.bg_circle_9f9f9f_bbbbbb)
|
||||
itemBinding.ivCrown.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
val messageHorizontalPadding = 8.dpToPx().toInt()
|
||||
val messageVerticalPadding = 5.3f.dpToPx().toInt()
|
||||
itemBinding.llMessageBg.setPadding(
|
||||
messageHorizontalPadding,
|
||||
messageVerticalPadding,
|
||||
messageHorizontalPadding,
|
||||
messageVerticalPadding
|
||||
)
|
||||
|
||||
if (SharedPreferenceManager.userId == userId) {
|
||||
itemBinding.llMessageBg.setBackgroundResource(R.drawable.bg_round_corner_3_3_999970ff)
|
||||
} else {
|
||||
itemBinding.llMessageBg.setBackgroundResource(R.drawable.bg_round_corner_3_3_99000000)
|
||||
}
|
||||
itemBinding.ivCoin.visibility = View.GONE
|
||||
itemBinding.tvDonationMessage.visibility = View.GONE
|
||||
itemBinding.root.setBackgroundResource(0)
|
||||
itemBinding.root.setPadding(0)
|
||||
}
|
||||
}
|
||||
|
||||
data class LiveRoomDonationChat(
|
||||
@SerializedName("profileUrl") val profileUrl: String,
|
||||
@SerializedName("nickname") val nickname: String,
|
||||
@SerializedName("chat") val chat: String,
|
||||
@SerializedName("coin") val coin: Int,
|
||||
@SerializedName("donationMessage") val donationMessage: String,
|
||||
) : LiveRoomChat() {
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bind(context: Context, binding: ViewBinding, onClickProfile: ((Long) -> Unit)?) {
|
||||
val itemBinding = binding as ItemLiveRoomChatBinding
|
||||
val spChat = SpannableString(chat)
|
||||
spChat.setSpan(
|
||||
ForegroundColorSpan(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.color_fdca2f
|
||||
)
|
||||
),
|
||||
0,
|
||||
chat.indexOf("코인", 0, true) + 2,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
|
||||
val spNickname = SpannableString("${nickname}님이")
|
||||
spNickname.setSpan(
|
||||
CustomTypefaceSpan(
|
||||
ResourcesCompat.getFont(
|
||||
context,
|
||||
R.font.gmarket_sans_medium
|
||||
)
|
||||
),
|
||||
0,
|
||||
nickname.length,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
|
||||
itemBinding.ivProfile.load(profileUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(RoundedCornersTransformation(23.3f.dpToPx()))
|
||||
}
|
||||
itemBinding.tvChat.text = spChat
|
||||
itemBinding.tvNickname.text = spNickname
|
||||
itemBinding.ivProfile.setOnClickListener {}
|
||||
|
||||
itemBinding.ivCoin.visibility = View.VISIBLE
|
||||
itemBinding.ivBg.visibility = View.GONE
|
||||
itemBinding.ivCrown.visibility = View.GONE
|
||||
itemBinding.tvCreatorOrManager.visibility = View.GONE
|
||||
|
||||
if (donationMessage.isNotBlank()) {
|
||||
itemBinding.tvDonationMessage.text = "\"$donationMessage\""
|
||||
itemBinding.tvDonationMessage.visibility = View.VISIBLE
|
||||
} else {
|
||||
itemBinding.tvDonationMessage.visibility = View.GONE
|
||||
}
|
||||
|
||||
itemBinding.llMessageBg.setPadding(0)
|
||||
itemBinding.llMessageBg.background = null
|
||||
|
||||
itemBinding.root.setBackgroundResource(
|
||||
when {
|
||||
coin >= 100000 -> {
|
||||
R.drawable.bg_round_corner_6_7_c25264
|
||||
}
|
||||
|
||||
coin >= 50000 -> {
|
||||
R.drawable.bg_round_corner_6_7_e6d85e37
|
||||
}
|
||||
|
||||
coin >= 10000 -> {
|
||||
R.drawable.bg_round_corner_6_7_e6d38c38
|
||||
}
|
||||
|
||||
coin >= 5000 -> {
|
||||
R.drawable.bg_round_corner_6_7_e659548f
|
||||
}
|
||||
|
||||
coin >= 1000 -> {
|
||||
R.drawable.bg_round_corner_6_7_e64d6aa4
|
||||
}
|
||||
|
||||
coin >= 500 -> {
|
||||
R.drawable.bg_round_corner_6_7_e62d7390
|
||||
}
|
||||
|
||||
else -> {
|
||||
R.drawable.bg_round_corner_6_7_e6548f7d
|
||||
}
|
||||
}
|
||||
)
|
||||
itemBinding.root.setPadding(33)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package kr.co.vividnext.sodalive.live.room.chat
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomChatBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomDonationStatusChatBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomJoinChatBinding
|
||||
|
||||
class LiveRoomChatAdapter(
|
||||
private val onClickProfile: (Long) -> Unit
|
||||
) : RecyclerView.Adapter<LiveRoomChatViewHolder>() {
|
||||
|
||||
val items = mutableListOf<LiveRoomChat>()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LiveRoomChatViewHolder {
|
||||
when (viewType) {
|
||||
LiveRoomChatType.DONATION_STATUS.ordinal -> {
|
||||
return LiveRoomDonationStatusChatViewHolder(
|
||||
parent.context,
|
||||
ItemLiveRoomDonationStatusChatBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
LiveRoomChatType.JOIN.ordinal -> {
|
||||
return LiveRoomJoinChatViewHolder(
|
||||
parent.context,
|
||||
ItemLiveRoomJoinChatBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
return LiveRoomNormalChatViewHolder(
|
||||
parent.context,
|
||||
ItemLiveRoomChatBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: LiveRoomChatViewHolder, position: Int) {
|
||||
holder.bind(items[position], onClickProfile)
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.count()
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return items[position].type.ordinal
|
||||
}
|
||||
}
|
||||
|
||||
abstract class LiveRoomChatViewHolder(binding: ViewBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
abstract fun bind(chat: LiveRoomChat, onClickProfile: ((Long) -> Unit)? = null)
|
||||
}
|
||||
|
||||
class LiveRoomNormalChatViewHolder(
|
||||
private val context: Context,
|
||||
private val binding: ItemLiveRoomChatBinding
|
||||
) : LiveRoomChatViewHolder(binding) {
|
||||
override fun bind(
|
||||
chat: LiveRoomChat,
|
||||
onClickProfile: ((Long) -> Unit)?
|
||||
) = chat.bind(context, binding, onClickProfile)
|
||||
}
|
||||
|
||||
class LiveRoomDonationStatusChatViewHolder(
|
||||
private val context: Context,
|
||||
private val binding: ItemLiveRoomDonationStatusChatBinding
|
||||
) : LiveRoomChatViewHolder(binding) {
|
||||
override fun bind(
|
||||
chat: LiveRoomChat,
|
||||
onClickProfile: ((Long) -> Unit)?
|
||||
) = chat.bind(context, binding)
|
||||
}
|
||||
|
||||
class LiveRoomJoinChatViewHolder(
|
||||
private val context: Context,
|
||||
private val binding: ItemLiveRoomJoinChatBinding
|
||||
) : LiveRoomChatViewHolder(binding) {
|
||||
override fun bind(
|
||||
chat: LiveRoomChat,
|
||||
onClickProfile: ((Long) -> Unit)?
|
||||
) = chat.bind(context, binding)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package kr.co.vividnext.sodalive.live.room.chat
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LiveRoomChatRawMessage(
|
||||
@SerializedName("type") val type: LiveRoomChatRawMessageType,
|
||||
@SerializedName("message") val message: String,
|
||||
@SerializedName("can") val can: Int,
|
||||
@SerializedName("donationMessage") val donationMessage: String?
|
||||
)
|
||||
|
||||
enum class LiveRoomChatRawMessageType {
|
||||
@SerializedName("DONATION") DONATION,
|
||||
@SerializedName("SET_MANAGER") SET_MANAGER,
|
||||
@SerializedName("EDIT_ROOM_INFO") EDIT_ROOM_INFO,
|
||||
@SerializedName("DONATION_STATUS") DONATION_STATUS
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DeleteLiveRoomDonationMessage(
|
||||
@SerializedName("roomId") val roomId: Long,
|
||||
@SerializedName("messageUUID") val messageUUID: String
|
||||
)
|
|
@ -0,0 +1,16 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class GetLiveRoomDonationStatusResponse(
|
||||
@SerializedName("donationList") val donationList: List<GetLiveRoomDonationItem>,
|
||||
@SerializedName("totalCount") val totalCount: Int,
|
||||
@SerializedName("totalCan") val totalCan: Int
|
||||
)
|
||||
|
||||
data class GetLiveRoomDonationItem(
|
||||
@SerializedName("profileImage") val profileImage: String,
|
||||
@SerializedName("nickname") val nickname: String,
|
||||
@SerializedName("userId") val userId: Long,
|
||||
@SerializedName("can") val can: Int
|
||||
)
|
|
@ -0,0 +1,7 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class GetLiveRoomDonationTotalResponse(
|
||||
@SerializedName("totalDonationCan") val totalDonationCan: Int
|
||||
)
|
|
@ -0,0 +1,108 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.WindowManager
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import coil.load
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomDonationBinding
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
|
||||
|
||||
class LiveRoomDonationDialog(
|
||||
private val activity: AppCompatActivity,
|
||||
layoutInflater: LayoutInflater,
|
||||
onClickDonation: (Int, String) -> Unit
|
||||
) {
|
||||
|
||||
private val bottomSheetDialog: BottomSheetDialog = BottomSheetDialog(activity)
|
||||
private val dialogView = DialogLiveRoomDonationBinding.inflate(layoutInflater)
|
||||
|
||||
init {
|
||||
bottomSheetDialog.setContentView(dialogView.root)
|
||||
bottomSheetDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
bottomSheetDialog.setCancelable(false)
|
||||
|
||||
dialogView.tvCancel.setOnClickListener { bottomSheetDialog.dismiss() }
|
||||
dialogView.tvDonation.setOnClickListener {
|
||||
try {
|
||||
val coin = dialogView.etDonationCoin.text.toString().toInt()
|
||||
val message = dialogView.etDonationMessage.text.toString()
|
||||
|
||||
if (coin > 0) {
|
||||
bottomSheetDialog.dismiss()
|
||||
onClickDonation(coin, message)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
"1코인 이상 후원하실 수 있습니다.",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
} catch (e: NumberFormatException) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
"1코인 이상 후원하실 수 있습니다.",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
setupView()
|
||||
}
|
||||
|
||||
fun show(width: Int) {
|
||||
if (!bottomSheetDialog.isShowing) {
|
||||
bottomSheetDialog.show()
|
||||
|
||||
val lp = WindowManager.LayoutParams()
|
||||
lp.copyFrom(bottomSheetDialog.window?.attributes)
|
||||
lp.width = width
|
||||
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
|
||||
bottomSheetDialog.window?.attributes = lp
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun setupView() {
|
||||
dialogView.tvCoin.text = SharedPreferenceManager.can.moneyFormat()
|
||||
dialogView.tvPlus10.setOnClickListener { addCoin(10) }
|
||||
dialogView.tvPlus100.setOnClickListener { addCoin(100) }
|
||||
dialogView.tvPlus1000.setOnClickListener { addCoin(1000) }
|
||||
dialogView.tvPlus10000.setOnClickListener { addCoin(10000) }
|
||||
|
||||
dialogView.ivProfile.load(SharedPreferenceManager.profileImage) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(CircleCropTransformation())
|
||||
}
|
||||
|
||||
dialogView.tvCoin.setOnClickListener {
|
||||
bottomSheetDialog.dismiss()
|
||||
|
||||
val intent = Intent(activity, CanChargeActivity::class.java)
|
||||
intent.putExtra(Constants.EXTRA_PREV_LIVE_ROOM, true)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun addCoin(coin: Int) {
|
||||
try {
|
||||
val currentCoin = dialogView.etDonationCoin.text.toString().toInt()
|
||||
dialogView.etDonationCoin.setText((currentCoin + coin).toString())
|
||||
} catch (e: NumberFormatException) {
|
||||
dialogView.etDonationCoin.setText(coin.toString())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LiveRoomDonationMessage(
|
||||
@SerializedName("uuid") val uuid: String,
|
||||
@SerializedName("nickname") val nickname: String,
|
||||
@SerializedName("canMessage") val canMessage: String,
|
||||
@SerializedName("donationMessage") val donationMessage: String
|
||||
)
|
|
@ -0,0 +1,62 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomDonationMessageBinding
|
||||
|
||||
class LiveRoomDonationMessageAdapter(
|
||||
private val onClickDeleteMessage: (String) -> Unit,
|
||||
private val copyMessage: (String) -> Unit
|
||||
) : RecyclerView.Adapter<LiveRoomDonationMessageAdapter.ViewHolder>() {
|
||||
|
||||
private var items = mutableListOf<LiveRoomDonationMessage>()
|
||||
|
||||
inner class ViewHolder(
|
||||
private val binding: ItemLiveRoomDonationMessageBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(item: LiveRoomDonationMessage) {
|
||||
binding.tvNickname.text = "${item.nickname}님이"
|
||||
binding.tvCoinMessage.text = item.canMessage
|
||||
binding.tvDonationMessage.text = "\"${item.donationMessage}\""
|
||||
binding.ivDelete.setOnClickListener { onClickDeleteMessage(item.uuid) }
|
||||
|
||||
binding.root.setOnClickListener { copyMessage(item.donationMessage) }
|
||||
}
|
||||
}
|
||||
|
||||
fun update(items: List<LiveRoomDonationMessage>) {
|
||||
items.let {
|
||||
val diffCallback = LiveRoomDonationMessageDiffUtilCallback(this.items, items)
|
||||
val diffResult = DiffUtil.calculateDiff(diffCallback)
|
||||
|
||||
this.items.run {
|
||||
clear()
|
||||
addAll(it)
|
||||
diffResult.dispatchUpdatesTo(this@LiveRoomDonationMessageAdapter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
ItemLiveRoomDonationMessageBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(items[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return items.size
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomDonationMessageBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
|
||||
class LiveRoomDonationMessageDialog(
|
||||
layoutInflater: LayoutInflater,
|
||||
private val activity: FragmentActivity,
|
||||
private val donationMessageListLiveData: LiveData<List<LiveRoomDonationMessage>>,
|
||||
private val donationMessageCountLiveData: LiveData<Int>,
|
||||
private val getDonationMessageList: () -> Unit,
|
||||
private val deleteDonationMessage: (String) -> Unit,
|
||||
copyMessage: (String) -> Unit
|
||||
) {
|
||||
private val alertDialog: AlertDialog
|
||||
private val dialogView = DialogLiveRoomDonationMessageBinding.inflate(layoutInflater)
|
||||
private val adapter = LiveRoomDonationMessageAdapter(
|
||||
onClickDeleteMessage = { deleteDonationMessage(it) },
|
||||
copyMessage = copyMessage
|
||||
)
|
||||
|
||||
init {
|
||||
val dialogBuilder = AlertDialog.Builder(activity)
|
||||
dialogBuilder.setView(dialogView.root)
|
||||
|
||||
alertDialog = dialogBuilder.create()
|
||||
alertDialog.setCancelable(false)
|
||||
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
|
||||
alertDialog.setOnShowListener {
|
||||
val lp = WindowManager.LayoutParams()
|
||||
lp.copyFrom(alertDialog.window?.attributes)
|
||||
lp.width = activity.resources.displayMetrics.widthPixels - (26.7f.dpToPx()).toInt()
|
||||
lp.height = activity.resources.displayMetrics.heightPixels - (200f.dpToPx()).toInt()
|
||||
|
||||
alertDialog.window?.attributes = lp
|
||||
}
|
||||
|
||||
dialogView.tvClose.setOnClickListener { alertDialog.dismiss() }
|
||||
setupRecyclerView()
|
||||
bindData()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
dialogView.rvDonationMessage.isNestedScrollingEnabled = true
|
||||
dialogView.rvDonationMessage.layoutManager = LinearLayoutManager(activity)
|
||||
dialogView.rvDonationMessage.addItemDecoration(object : RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
super.getItemOffsets(outRect, view, parent, state)
|
||||
|
||||
when (parent.getChildAdapterPosition(view)) {
|
||||
0 -> {
|
||||
outRect.top = 0.dpToPx().toInt()
|
||||
outRect.bottom = 4.dpToPx().toInt()
|
||||
}
|
||||
|
||||
adapter.itemCount - 1 -> {
|
||||
outRect.top = 4.dpToPx().toInt()
|
||||
outRect.bottom = 0.dpToPx().toInt()
|
||||
}
|
||||
|
||||
else -> {
|
||||
outRect.top = 4.dpToPx().toInt()
|
||||
outRect.bottom = 4.dpToPx().toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
dialogView.rvDonationMessage.adapter = adapter
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun bindData() {
|
||||
donationMessageListLiveData.observe(activity) {
|
||||
if (it.isEmpty()) {
|
||||
dialogView.rvDonationMessage.visibility = View.GONE
|
||||
dialogView.tvNone.visibility = View.VISIBLE
|
||||
} else {
|
||||
dialogView.rvDonationMessage.visibility = View.VISIBLE
|
||||
dialogView.tvNone.visibility = View.GONE
|
||||
}
|
||||
|
||||
adapter.update(it)
|
||||
}
|
||||
|
||||
donationMessageCountLiveData.observe(activity) {
|
||||
dialogView.tvDonationMessageCount.text = "(${it.moneyFormat()})"
|
||||
}
|
||||
}
|
||||
|
||||
fun show() {
|
||||
alertDialog.show()
|
||||
getDonationMessageList()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
|
||||
class LiveRoomDonationMessageDiffUtilCallback(
|
||||
private val oldData: List<LiveRoomDonationMessage>,
|
||||
private val newData: List<LiveRoomDonationMessage>
|
||||
) : DiffUtil.Callback() {
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val oldItem = oldData[oldItemPosition]
|
||||
val newItem = newData[newItemPosition]
|
||||
|
||||
return oldItem.uuid == newItem.uuid
|
||||
}
|
||||
|
||||
override fun getOldListSize(): Int = oldData.size
|
||||
|
||||
override fun getNewListSize(): Int = newData.size
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItemPosition: Int,
|
||||
newItemPosition: Int
|
||||
) = oldData[oldItemPosition] == newData[newItemPosition]
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.orhanobut.logger.Logger
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.live.LiveRepository
|
||||
|
||||
class LiveRoomDonationMessageViewModel(private val repository: LiveRepository) : BaseViewModel() {
|
||||
private val _donationMessageListLiveData = MutableLiveData<List<LiveRoomDonationMessage>>()
|
||||
val donationMessageListLiveData: LiveData<List<LiveRoomDonationMessage>>
|
||||
get() = _donationMessageListLiveData
|
||||
|
||||
private val _donationMessageCountLiveData = MutableLiveData(0)
|
||||
val donationMessageCountLiveData: LiveData<Int>
|
||||
get() = _donationMessageCountLiveData
|
||||
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
fun getDonationMessageList(roomId: Long) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.getDonationMessageList(
|
||||
roomId = roomId,
|
||||
token = "Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success && it.data != null) {
|
||||
_donationMessageListLiveData.postValue(it.data!!)
|
||||
_donationMessageCountLiveData.postValue(it.data!!.size)
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun deleteDonationMessage(roomId: Long, uuid: String) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.deleteDonationMessage(
|
||||
roomId = roomId,
|
||||
uuid = uuid,
|
||||
token = "Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success) {
|
||||
getDonationMessageList(roomId)
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
import coil.transform.CircleCropTransformation
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomDonationRankingBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
|
||||
class LiveRoomDonationRankingAdapter :
|
||||
RecyclerView.Adapter<LiveRoomDonationRankingAdapter.ViewHolder>() {
|
||||
val items = mutableListOf<GetLiveRoomDonationItem>()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
|
||||
parent.context,
|
||||
ItemLiveRoomDonationRankingBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(items[position], position)
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.count()
|
||||
|
||||
inner class ViewHolder(
|
||||
private val context: Context,
|
||||
private val binding: ItemLiveRoomDonationRankingBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(item: GetLiveRoomDonationItem, position: Int) {
|
||||
binding.tvRank.text = "${position + 1}"
|
||||
binding.tvNickname.text = item.nickname
|
||||
|
||||
binding.ivProfile.load(item.profileImage) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(CircleCropTransformation())
|
||||
}
|
||||
|
||||
binding.tvDonationCan.text = item.can.moneyFormat()
|
||||
|
||||
val lp = binding.rlDonationRanking.layoutParams as FrameLayout.LayoutParams
|
||||
|
||||
when (position) {
|
||||
0 -> {
|
||||
binding.ivBg.setImageResource(R.drawable.bg_circle_ffdc00_ffb600)
|
||||
binding.ivBg.visibility = View.VISIBLE
|
||||
|
||||
binding.ivCrown.setImageResource(R.drawable.ic_crown_1)
|
||||
binding.ivCrown.visibility = View.VISIBLE
|
||||
binding.root.setBackgroundResource(
|
||||
if (items.size == 1) {
|
||||
R.drawable.bg_round_corner_4_7_2b2635
|
||||
} else {
|
||||
R.drawable.bg_top_round_corner_4_7_2b2635
|
||||
}
|
||||
)
|
||||
|
||||
lp.setMargins(
|
||||
0,
|
||||
20.dpToPx().toInt(),
|
||||
0,
|
||||
if (items.size == 1) {
|
||||
20.dpToPx().toInt()
|
||||
} else {
|
||||
13.3f.dpToPx().toInt()
|
||||
}
|
||||
)
|
||||
binding.rlDonationRanking.layoutParams = lp
|
||||
}
|
||||
|
||||
1 -> {
|
||||
binding.ivBg.setImageResource(R.drawable.bg_circle_ffffff_9f9f9f)
|
||||
binding.ivBg.visibility = View.VISIBLE
|
||||
|
||||
binding.ivCrown.setImageResource(R.drawable.ic_crown_2)
|
||||
binding.ivCrown.visibility = View.VISIBLE
|
||||
|
||||
if (items.size == 2) {
|
||||
binding.root.setBackgroundResource(
|
||||
R.drawable.bg_bottom_round_corner_4_7_2b2635
|
||||
)
|
||||
} else {
|
||||
binding.root.setBackgroundColor(
|
||||
ContextCompat.getColor(context, R.color.color_2b2635)
|
||||
)
|
||||
}
|
||||
|
||||
lp.setMargins(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
if (items.size == 2) {
|
||||
20.dpToPx().toInt()
|
||||
} else {
|
||||
13.3f.dpToPx().toInt()
|
||||
}
|
||||
)
|
||||
binding.rlDonationRanking.layoutParams = lp
|
||||
}
|
||||
|
||||
2 -> {
|
||||
binding.ivBg.setImageResource(R.drawable.bg_circle_e6a77a_c67e4a)
|
||||
binding.ivBg.visibility = View.VISIBLE
|
||||
|
||||
binding.ivCrown.setImageResource(R.drawable.ic_crown_3)
|
||||
binding.ivCrown.visibility = View.VISIBLE
|
||||
binding.root.setBackgroundResource(
|
||||
R.drawable.bg_bottom_round_corner_4_7_2b2635
|
||||
)
|
||||
|
||||
lp.setMargins(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
20.dpToPx().toInt()
|
||||
)
|
||||
binding.rlDonationRanking.layoutParams = lp
|
||||
}
|
||||
|
||||
else -> {
|
||||
binding.ivBg.setImageResource(0)
|
||||
binding.ivBg.visibility = View.GONE
|
||||
binding.ivCrown.visibility = View.GONE
|
||||
binding.root.setBackgroundResource(0)
|
||||
binding.root.background = null
|
||||
|
||||
lp.setMargins(0, 0, 0, 0)
|
||||
binding.rlDonationRanking.layoutParams = lp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Rect
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomDonationRankingBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomViewModel
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
class LiveRoomDonationRankingDialog(
|
||||
private val activity: FragmentActivity,
|
||||
layoutInflater: LayoutInflater,
|
||||
viewModel: LiveRoomViewModel,
|
||||
roomId: Long
|
||||
) {
|
||||
private val bottomSheetDialog: BottomSheetDialog = BottomSheetDialog(activity)
|
||||
private val dialogView = DialogLiveRoomDonationRankingBinding.inflate(layoutInflater)
|
||||
private val adapter = LiveRoomDonationRankingAdapter()
|
||||
|
||||
init {
|
||||
bottomSheetDialog.setContentView(dialogView.root)
|
||||
bottomSheetDialog.setCancelable(false)
|
||||
|
||||
dialogView.ivClose.setOnClickListener { bottomSheetDialog.dismiss() }
|
||||
setupRecyclerView()
|
||||
|
||||
viewModel.donationStatus(roomId) {
|
||||
adapter.items.clear()
|
||||
adapter.items.addAll(it.donationList)
|
||||
adapter.notifyDataSetChanged()
|
||||
dialogView.tvTotalCoin.text = it.totalCan.moneyFormat()
|
||||
dialogView.tvTotalCount.text = it.totalCount.moneyFormat()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
dialogView.rvDonationRanking.layoutManager = LinearLayoutManager(
|
||||
activity,
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false
|
||||
)
|
||||
|
||||
dialogView.rvDonationRanking.addItemDecoration(object : RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
super.getItemOffsets(outRect, view, parent, state)
|
||||
|
||||
outRect.left = 13.3f.dpToPx().toInt()
|
||||
outRect.right = 13.3f.dpToPx().toInt()
|
||||
|
||||
when (parent.getChildAdapterPosition(view)) {
|
||||
0, 1, 2 -> {
|
||||
outRect.top = 0
|
||||
outRect.bottom = 0
|
||||
}
|
||||
|
||||
3 -> {
|
||||
outRect.top = 20.dpToPx().toInt()
|
||||
outRect.bottom = 6.7f.dpToPx().toInt()
|
||||
}
|
||||
|
||||
else -> {
|
||||
outRect.top = 6.7f.dpToPx().toInt()
|
||||
outRect.bottom = 6.7f.dpToPx().toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
dialogView.rvDonationRanking.adapter = adapter
|
||||
}
|
||||
|
||||
fun show() {
|
||||
bottomSheetDialog.show()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LiveRoomDonationRequest(
|
||||
@SerializedName("roomId") val roomId: Long,
|
||||
@SerializedName("can") val can: Int,
|
||||
@SerializedName("message") val message: String,
|
||||
@SerializedName("container") val container: String
|
||||
)
|
|
@ -0,0 +1,26 @@
|
|||
package kr.co.vividnext.sodalive.live.room.info
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class GetRoomInfoResponse(
|
||||
@SerializedName("roomId") val roomId: Long,
|
||||
@SerializedName("title") val title: String,
|
||||
@SerializedName("notice") val notice: String,
|
||||
@SerializedName("coverImageUrl") val coverImageUrl: String,
|
||||
@SerializedName("channelName") val channelName: String,
|
||||
@SerializedName("rtcToken") val rtcToken: String,
|
||||
@SerializedName("rtmToken") val rtmToken: String,
|
||||
@SerializedName("managerId") val managerId: Long,
|
||||
@SerializedName("managerNickname") val managerNickname: String,
|
||||
@SerializedName("managerProfileUrl") val managerProfileUrl: String,
|
||||
@SerializedName("isFollowingManager") val isFollowingManager: Boolean,
|
||||
@SerializedName("participantsCount") val participantsCount: Int,
|
||||
@SerializedName("totalAvailableParticipantsCount") val totalAvailableParticipantsCount: Int,
|
||||
@SerializedName("speakerList") val speakerList: List<LiveRoomMember>,
|
||||
@SerializedName("listenerList") val listenerList: List<LiveRoomMember>,
|
||||
@SerializedName("managerList") val managerList: List<LiveRoomMember>,
|
||||
@SerializedName("donationRankingTop3UserIds") val donationRankingTop3UserIds: List<Long>,
|
||||
@SerializedName("isAvailableDonation") val isAvailableDonation: Boolean = false,
|
||||
@SerializedName("isPrivateRoom") val isPrivateRoom: Boolean,
|
||||
@SerializedName("password") val password: String? = null
|
||||
)
|
|
@ -0,0 +1,19 @@
|
|||
package kr.co.vividnext.sodalive.live.room.info
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LiveRoomMember(
|
||||
@SerializedName("id") val id: Long,
|
||||
@SerializedName("nickname") val nickname: String,
|
||||
@SerializedName("profileImage") val profileImage: String,
|
||||
@SerializedName("role") val role: LiveRoomMemberRole
|
||||
)
|
||||
|
||||
enum class LiveRoomMemberRole {
|
||||
@SerializedName("LISTENER")
|
||||
LISTENER,
|
||||
@SerializedName("SPEAKER")
|
||||
SPEAKER,
|
||||
@SerializedName("MANAGER")
|
||||
MANAGER
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package kr.co.vividnext.sodalive.live.room.kick_out
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LiveRoomKickOutRequest(
|
||||
@SerializedName("roomId") val roomId: Long,
|
||||
@SerializedName("userId") val userId: Long
|
||||
)
|
|
@ -0,0 +1,20 @@
|
|||
package kr.co.vividnext.sodalive.live.room.profile
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class GetLiveRoomUserProfileResponse(
|
||||
@SerializedName("userId") val userId: Long,
|
||||
@SerializedName("nickname") val nickname: String,
|
||||
@SerializedName("profileUrl") val profileUrl: String,
|
||||
@SerializedName("gender") val gender: String,
|
||||
@SerializedName("instagramUrl") val instagramUrl: String,
|
||||
@SerializedName("youtubeUrl") val youtubeUrl: String,
|
||||
@SerializedName("websiteUrl") val websiteUrl: String,
|
||||
@SerializedName("blogUrl") val blogUrl: String,
|
||||
@SerializedName("introduce") val introduce: String,
|
||||
@SerializedName("tags") val tags: String,
|
||||
@SerializedName("isSpeaker") val isSpeaker: Boolean?,
|
||||
@SerializedName("isManager") val isManager: Boolean?,
|
||||
@SerializedName("isFollowing") val isFollowing: Boolean?,
|
||||
@SerializedName("isBlock") val isBlock: Boolean
|
||||
)
|
|
@ -0,0 +1,27 @@
|
|||
package kr.co.vividnext.sodalive.live.room.profile
|
||||
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import kr.co.vividnext.sodalive.live.room.info.LiveRoomMember
|
||||
|
||||
class LiveRoomMemberResponseDiffUtilCallback(
|
||||
private val oldData: List<Any>,
|
||||
private val newData: List<Any>
|
||||
) : DiffUtil.Callback() {
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val oldItem = oldData[oldItemPosition]
|
||||
val newItem = newData[newItemPosition]
|
||||
|
||||
return if (oldItem is LiveRoomMember && newItem is LiveRoomMember) {
|
||||
oldItem.id == newItem.id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun getOldListSize(): Int = oldData.size
|
||||
|
||||
override fun getNewListSize(): Int = newData.size
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
|
||||
oldData[oldItemPosition] == newData[newItemPosition]
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
package kr.co.vividnext.sodalive.live.room.profile
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomListProfileBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomProfileHeaderBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomProfileManagerBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomProfileMasterBinding
|
||||
import kr.co.vividnext.sodalive.live.room.info.LiveRoomMember
|
||||
|
||||
class LiveRoomProfileAdapter(
|
||||
private val isStaff: () -> Boolean,
|
||||
private val onClickInviteSpeaker: (Long) -> Unit,
|
||||
private val onClickChangeListener: (Long) -> Unit,
|
||||
private val kickOut: (Long) -> Unit,
|
||||
private val onClickProfile: (Long) -> Unit
|
||||
) : RecyclerView.Adapter<LiveRoomProfileViewHolder>() {
|
||||
|
||||
private val items = mutableListOf<LiveRoomProfileItem>()
|
||||
var managerId: Long = -1
|
||||
var totalUserCount: Int = 0
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun updateList(
|
||||
speakers: List<LiveRoomMember>,
|
||||
listeners: List<LiveRoomMember>,
|
||||
managers: List<LiveRoomMember>
|
||||
) {
|
||||
val items = mutableListOf<LiveRoomProfileItem>()
|
||||
speakers.forEach {
|
||||
if (it.id == managerId) {
|
||||
val item = LiveRoomProfileItemMaster(
|
||||
nickname = it.nickname,
|
||||
profileUrl = it.profileImage
|
||||
)
|
||||
item.id = it.id
|
||||
|
||||
items.add(0, item)
|
||||
} else {
|
||||
val item = LiveRoomProfileItemUser(
|
||||
nickname = it.nickname,
|
||||
profileUrl = it.profileImage,
|
||||
role = it.role
|
||||
)
|
||||
|
||||
item.id = it.id
|
||||
items.add(item)
|
||||
}
|
||||
}
|
||||
|
||||
items.add(
|
||||
1,
|
||||
LiveRoomProfileItemSpeakerTitle(
|
||||
"스피커",
|
||||
speakerCount = speakers.size - 1,
|
||||
totalUserCount = totalUserCount
|
||||
)
|
||||
)
|
||||
|
||||
items.add(
|
||||
1,
|
||||
LiveRoomProfileItemManagerTitle(
|
||||
"스탭",
|
||||
managerCount = managers.size
|
||||
)
|
||||
)
|
||||
|
||||
managers.forEachIndexed { index, manager ->
|
||||
val item = LiveRoomProfileItemManager(
|
||||
nickname = manager.nickname,
|
||||
profileUrl = manager.profileImage
|
||||
)
|
||||
|
||||
item.id = manager.id
|
||||
items.add(index = index + 2, item)
|
||||
}
|
||||
|
||||
items.add(LiveRoomProfileItemListenerTitle("리스너"))
|
||||
|
||||
listeners.forEach {
|
||||
val item = LiveRoomProfileItemUser(
|
||||
nickname = it.nickname,
|
||||
profileUrl = it.profileImage,
|
||||
role = it.role
|
||||
)
|
||||
|
||||
item.id = it.id
|
||||
items.add(item)
|
||||
}
|
||||
|
||||
this.items.run {
|
||||
clear()
|
||||
addAll(items)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LiveRoomProfileViewHolder {
|
||||
when (viewType) {
|
||||
LiveRoomProfileItemType.SPEAKER_TITLE.ordinal -> {
|
||||
return LiveRoomProfileSpeakerTitleViewHolder(
|
||||
ItemLiveRoomProfileHeaderBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
LiveRoomProfileItemType.MANAGER_TITLE.ordinal -> {
|
||||
return LiveRoomProfileManagerTitleViewHolder(
|
||||
ItemLiveRoomProfileHeaderBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
LiveRoomProfileItemType.LISTENER_TITLE.ordinal -> {
|
||||
return LiveRoomProfileListenerTitleViewHolder(
|
||||
ItemLiveRoomProfileHeaderBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
LiveRoomProfileItemType.MASTER.ordinal -> {
|
||||
return LiveRoomProfileMasterViewHolder(
|
||||
ItemLiveRoomProfileMasterBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
),
|
||||
managerId,
|
||||
onClickProfile = onClickProfile
|
||||
)
|
||||
}
|
||||
|
||||
LiveRoomProfileItemType.MANAGER.ordinal -> {
|
||||
return LiveRoomProfileManagerViewHolder(
|
||||
ItemLiveRoomProfileManagerBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
),
|
||||
onClickProfile = onClickProfile
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
return LiveRoomProfileUserViewHolder(
|
||||
ItemLiveRoomListProfileBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
),
|
||||
isStaff = isStaff,
|
||||
managerId = managerId,
|
||||
onClickInviteSpeaker = onClickInviteSpeaker,
|
||||
onClickChangeListener = onClickChangeListener,
|
||||
kickOut = kickOut,
|
||||
onClickProfile = onClickProfile
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: LiveRoomProfileViewHolder, position: Int) {
|
||||
holder.bind(items[position])
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return items[position].type.ordinal
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
}
|
||||
|
||||
abstract class LiveRoomProfileViewHolder(binding: ViewBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
abstract fun bind(item: LiveRoomProfileItem)
|
||||
}
|
||||
|
||||
class LiveRoomProfileSpeakerTitleViewHolder(
|
||||
private val binding: ItemLiveRoomProfileHeaderBinding,
|
||||
) : LiveRoomProfileViewHolder(binding) {
|
||||
override fun bind(item: LiveRoomProfileItem) = item.bind(binding)
|
||||
}
|
||||
|
||||
class LiveRoomProfileListenerTitleViewHolder(
|
||||
private val binding: ItemLiveRoomProfileHeaderBinding,
|
||||
) : LiveRoomProfileViewHolder(binding) {
|
||||
override fun bind(item: LiveRoomProfileItem) = item.bind(binding)
|
||||
}
|
||||
|
||||
class LiveRoomProfileManagerTitleViewHolder(
|
||||
private val binding: ItemLiveRoomProfileHeaderBinding,
|
||||
) : LiveRoomProfileViewHolder(binding) {
|
||||
override fun bind(item: LiveRoomProfileItem) = item.bind(binding)
|
||||
}
|
||||
|
||||
class LiveRoomProfileMasterViewHolder(
|
||||
private val binding: ItemLiveRoomProfileMasterBinding,
|
||||
private val managerId: Long,
|
||||
private val onClickProfile: (Long) -> Unit
|
||||
) : LiveRoomProfileViewHolder(binding) {
|
||||
override fun bind(item: LiveRoomProfileItem) {
|
||||
item.onClickProfile = onClickProfile
|
||||
item.managerId = managerId
|
||||
item.bind(binding)
|
||||
}
|
||||
}
|
||||
|
||||
class LiveRoomProfileManagerViewHolder(
|
||||
private val binding: ItemLiveRoomProfileManagerBinding,
|
||||
private val onClickProfile: (Long) -> Unit
|
||||
) : LiveRoomProfileViewHolder(binding) {
|
||||
override fun bind(item: LiveRoomProfileItem) {
|
||||
item.onClickProfile = onClickProfile
|
||||
item.bind(binding)
|
||||
}
|
||||
}
|
||||
|
||||
class LiveRoomProfileUserViewHolder(
|
||||
private val binding: ItemLiveRoomListProfileBinding,
|
||||
private val isStaff: () -> Boolean,
|
||||
private val managerId: Long,
|
||||
private val onClickInviteSpeaker: (Long) -> Unit,
|
||||
private val onClickChangeListener: (Long) -> Unit,
|
||||
private val kickOut: (Long) -> Unit,
|
||||
private val onClickProfile: (Long) -> Unit
|
||||
) : LiveRoomProfileViewHolder(binding) {
|
||||
override fun bind(item: LiveRoomProfileItem) {
|
||||
item.isStaff = isStaff
|
||||
item.managerId = managerId
|
||||
item.onClickInviteSpeaker = onClickInviteSpeaker
|
||||
item.onClickChangeListener = onClickChangeListener
|
||||
item.kickOut = kickOut
|
||||
item.onClickProfile = onClickProfile
|
||||
item.bind(binding)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package kr.co.vividnext.sodalive.live.room.profile
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomProfileBinding
|
||||
import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse
|
||||
|
||||
class LiveRoomProfileDialog(
|
||||
layoutInflater: LayoutInflater,
|
||||
private val activity: FragmentActivity,
|
||||
private val roomInfoLiveData: LiveData<GetRoomInfoResponse>,
|
||||
isStaff: () -> Boolean,
|
||||
onClickInviteSpeaker: (Long) -> Unit,
|
||||
onClickChangeListener: (Long) -> Unit,
|
||||
onClickKickOut: (Long) -> Unit,
|
||||
onClickProfile: (Long) -> Unit
|
||||
) {
|
||||
private val bottomSheetDialog: BottomSheetDialog = BottomSheetDialog(activity)
|
||||
private val dialogView = DialogLiveRoomProfileBinding.inflate(layoutInflater)
|
||||
private val adapter = LiveRoomProfileAdapter(
|
||||
isStaff = isStaff,
|
||||
onClickInviteSpeaker = onClickInviteSpeaker,
|
||||
onClickChangeListener = onClickChangeListener,
|
||||
kickOut = onClickKickOut,
|
||||
onClickProfile = onClickProfile
|
||||
)
|
||||
|
||||
init {
|
||||
bottomSheetDialog.setContentView(dialogView.root)
|
||||
bottomSheetDialog.setCancelable(false)
|
||||
|
||||
dialogView.ivClose.setOnClickListener { bottomSheetDialog.dismiss() }
|
||||
setupRecyclerView()
|
||||
bindData()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
dialogView.rvPeoples.isNestedScrollingEnabled = true
|
||||
dialogView.rvPeoples.layoutManager = LinearLayoutManager(
|
||||
activity,
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false
|
||||
)
|
||||
|
||||
dialogView.rvPeoples.adapter = adapter
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun bindData() {
|
||||
roomInfoLiveData.observe(activity) {
|
||||
adapter.managerId = it.managerId
|
||||
adapter.totalUserCount = it.totalAvailableParticipantsCount
|
||||
dialogView.tvParticipate.text = "${it.participantsCount}"
|
||||
dialogView.tvTotalPeoples.text = "/${it.totalAvailableParticipantsCount}"
|
||||
|
||||
adapter.updateList(
|
||||
speakers = it.speakerList,
|
||||
listeners = it.listenerList,
|
||||
managers = it.managerList
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun show() {
|
||||
bottomSheetDialog.show()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
package kr.co.vividnext.sodalive.live.room.profile
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import coil.load
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomListProfileBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomProfileHeaderBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomProfileManagerBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomProfileMasterBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.live.room.info.LiveRoomMemberRole
|
||||
|
||||
enum class LiveRoomProfileItemType {
|
||||
@SerializedName("MASTER")
|
||||
MASTER,
|
||||
|
||||
@SerializedName("MANAGER")
|
||||
MANAGER,
|
||||
|
||||
@SerializedName("SPEAKER_TITLE")
|
||||
SPEAKER_TITLE,
|
||||
|
||||
@SerializedName("LISTENER_TITLE")
|
||||
LISTENER_TITLE,
|
||||
|
||||
@SerializedName("MANAGER_TITLE")
|
||||
MANAGER_TITLE,
|
||||
|
||||
@SerializedName("USER")
|
||||
USER
|
||||
}
|
||||
|
||||
abstract class LiveRoomProfileItem {
|
||||
open var id: Long = 0L
|
||||
open var type: LiveRoomProfileItemType = LiveRoomProfileItemType.USER
|
||||
open var isStaff: () -> Boolean = { false }
|
||||
open var managerId = 0L
|
||||
open var onClickInviteSpeaker: (Long) -> Unit = {}
|
||||
open var onClickChangeListener: (Long) -> Unit = {}
|
||||
open var kickOut: (Long) -> Unit = {}
|
||||
open var onClickProfile: (Long) -> Unit = {}
|
||||
abstract fun bind(binding: ViewBinding)
|
||||
}
|
||||
|
||||
data class LiveRoomProfileItemSpeakerTitle(
|
||||
val title: String,
|
||||
val speakerCount: Int,
|
||||
val totalUserCount: Int
|
||||
) : LiveRoomProfileItem() {
|
||||
override var type = LiveRoomProfileItemType.SPEAKER_TITLE
|
||||
override fun bind(binding: ViewBinding) {
|
||||
val itemBinding = binding as ItemLiveRoomProfileHeaderBinding
|
||||
itemBinding.tvTitle.text = title
|
||||
itemBinding.tvSpeakerCount.text = "$speakerCount"
|
||||
itemBinding.tvSpeakerTotalCount.text = if (totalUserCount > 9) {
|
||||
"/9"
|
||||
} else {
|
||||
"/${totalUserCount - 1}"
|
||||
}
|
||||
itemBinding.tvSpeakerCount.visibility = View.VISIBLE
|
||||
itemBinding.tvSpeakerTotalCount.visibility = View.VISIBLE
|
||||
|
||||
val lp = itemBinding.llRoot.layoutParams as RecyclerView.LayoutParams
|
||||
lp.topMargin = 14f.dpToPx().toInt()
|
||||
lp.bottomMargin = 14f.dpToPx().toInt()
|
||||
itemBinding.llRoot.layoutParams = lp
|
||||
}
|
||||
}
|
||||
|
||||
data class LiveRoomProfileItemListenerTitle(
|
||||
val title: String
|
||||
) : LiveRoomProfileItem() {
|
||||
override var type = LiveRoomProfileItemType.LISTENER_TITLE
|
||||
override fun bind(binding: ViewBinding) {
|
||||
val itemBinding = binding as ItemLiveRoomProfileHeaderBinding
|
||||
itemBinding.tvTitle.text = title
|
||||
itemBinding.tvSpeakerCount.visibility = View.GONE
|
||||
itemBinding.tvSpeakerTotalCount.visibility = View.GONE
|
||||
|
||||
val lp = itemBinding.llRoot.layoutParams as RecyclerView.LayoutParams
|
||||
lp.topMargin = 20f.dpToPx().toInt()
|
||||
lp.bottomMargin = 14f.dpToPx().toInt()
|
||||
itemBinding.llRoot.layoutParams = lp
|
||||
}
|
||||
}
|
||||
|
||||
data class LiveRoomProfileItemManagerTitle(
|
||||
val title: String,
|
||||
val managerCount: Int
|
||||
) : LiveRoomProfileItem() {
|
||||
override var type = LiveRoomProfileItemType.MANAGER_TITLE
|
||||
override fun bind(binding: ViewBinding) {
|
||||
val itemBinding = binding as ItemLiveRoomProfileHeaderBinding
|
||||
itemBinding.tvTitle.text = title
|
||||
itemBinding.tvSpeakerCount.text = "$managerCount"
|
||||
itemBinding.tvSpeakerCount.visibility = View.VISIBLE
|
||||
itemBinding.tvSpeakerTotalCount.visibility = View.GONE
|
||||
|
||||
val lp = itemBinding.llRoot.layoutParams as RecyclerView.LayoutParams
|
||||
lp.topMargin = 14f.dpToPx().toInt()
|
||||
lp.bottomMargin = 14f.dpToPx().toInt()
|
||||
itemBinding.llRoot.layoutParams = lp
|
||||
}
|
||||
}
|
||||
|
||||
data class LiveRoomProfileItemMaster(
|
||||
val nickname: String,
|
||||
val profileUrl: String
|
||||
) : LiveRoomProfileItem() {
|
||||
override var type = LiveRoomProfileItemType.MASTER
|
||||
override fun bind(binding: ViewBinding) {
|
||||
val itemBinding = binding as ItemLiveRoomProfileMasterBinding
|
||||
itemBinding.tvNickname.text = nickname
|
||||
itemBinding.ivProfile.load(profileUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(CircleCropTransformation())
|
||||
}
|
||||
if (id != SharedPreferenceManager.userId) {
|
||||
itemBinding.ivProfile.setOnClickListener { onClickProfile(id) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class LiveRoomProfileItemManager(
|
||||
val nickname: String,
|
||||
val profileUrl: String
|
||||
) : LiveRoomProfileItem() {
|
||||
override var type = LiveRoomProfileItemType.MANAGER
|
||||
override fun bind(binding: ViewBinding) {
|
||||
val itemBinding = binding as ItemLiveRoomProfileManagerBinding
|
||||
itemBinding.tvNickname.text = nickname
|
||||
itemBinding.ivProfile.load(profileUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(CircleCropTransformation())
|
||||
}
|
||||
if (id != SharedPreferenceManager.userId) {
|
||||
itemBinding.ivProfile.setOnClickListener {
|
||||
onClickProfile(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class LiveRoomProfileItemUser(
|
||||
val nickname: String,
|
||||
val profileUrl: String,
|
||||
val role: LiveRoomMemberRole
|
||||
) : LiveRoomProfileItem() {
|
||||
override var type = LiveRoomProfileItemType.USER
|
||||
override fun bind(binding: ViewBinding) {
|
||||
val itemBinding = binding as ItemLiveRoomListProfileBinding
|
||||
itemBinding.tvNickname.text = nickname
|
||||
itemBinding.ivProfile.load(profileUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(CircleCropTransformation())
|
||||
}
|
||||
|
||||
if (id != SharedPreferenceManager.userId) {
|
||||
itemBinding.ivProfile.setOnClickListener { onClickProfile(id) }
|
||||
}
|
||||
|
||||
if (id == SharedPreferenceManager.userId && role == LiveRoomMemberRole.SPEAKER) {
|
||||
itemBinding.llControlButtons.visibility = View.VISIBLE
|
||||
itemBinding.tvChangeAudience.visibility = View.VISIBLE
|
||||
itemBinding.tvInviteSpeaker.visibility = View.GONE
|
||||
itemBinding.ivKickOut.visibility = View.GONE
|
||||
|
||||
itemBinding.tvChangeAudience.setOnClickListener { onClickChangeListener(id) }
|
||||
itemBinding.tvInviteSpeaker.setOnClickListener { }
|
||||
} else if (managerId == SharedPreferenceManager.userId || isStaff()) {
|
||||
itemBinding.llControlButtons.visibility = View.VISIBLE
|
||||
itemBinding.ivKickOut.visibility = View.VISIBLE
|
||||
|
||||
if (role == LiveRoomMemberRole.SPEAKER) {
|
||||
itemBinding.tvChangeAudience.visibility = View.VISIBLE
|
||||
itemBinding.tvInviteSpeaker.visibility = View.GONE
|
||||
|
||||
itemBinding.tvChangeAudience.setOnClickListener { onClickChangeListener(id) }
|
||||
itemBinding.tvInviteSpeaker.setOnClickListener { }
|
||||
} else {
|
||||
itemBinding.tvChangeAudience.visibility = View.GONE
|
||||
itemBinding.tvInviteSpeaker.visibility = View.VISIBLE
|
||||
|
||||
itemBinding.tvChangeAudience.setOnClickListener { }
|
||||
itemBinding.tvInviteSpeaker.setOnClickListener { onClickInviteSpeaker(id) }
|
||||
}
|
||||
|
||||
itemBinding.ivKickOut.setOnClickListener { kickOut(id) }
|
||||
} else {
|
||||
itemBinding.llControlButtons.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package kr.co.vividnext.sodalive.live.room.profile
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveRoomProfileBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.live.room.info.LiveRoomMember
|
||||
|
||||
class LiveRoomProfileListAdapter : RecyclerView.Adapter<LiveRoomProfileListAdapter.ViewHolder>() {
|
||||
inner class ViewHolder(
|
||||
private val binding: ItemLiveRoomProfileBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(item: LiveRoomMember) {
|
||||
binding.ivProfile.load(item.profileImage) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(RoundedCornersTransformation(23.3f.dpToPx()))
|
||||
|
||||
if (activeSpeakers.contains(item.id.toInt())) {
|
||||
binding.ivBg.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.ivBg.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (muteSpeakers.contains(item.id.toInt())) {
|
||||
binding.ivMute.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.ivMute.visibility = View.GONE
|
||||
}
|
||||
|
||||
val ivMuteLp = binding.ivMute.layoutParams
|
||||
ivMuteLp.width = 51.7f.dpToPx().toInt()
|
||||
ivMuteLp.height = 51.7f.dpToPx().toInt()
|
||||
binding.ivMute.layoutParams = ivMuteLp
|
||||
|
||||
if (managerId == item.id) {
|
||||
binding.ivCrown.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.ivCrown.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
binding.tvNickname.text = item.nickname
|
||||
}
|
||||
}
|
||||
|
||||
private val items = mutableListOf<LiveRoomMember>()
|
||||
val activeSpeakers = mutableSetOf<Int>()
|
||||
val muteSpeakers = mutableSetOf<Int>()
|
||||
var managerId: Long = -1
|
||||
|
||||
fun updateList(items: List<LiveRoomMember>) {
|
||||
items.let {
|
||||
val diffCallback = LiveRoomMemberResponseDiffUtilCallback(this.items, items)
|
||||
val diffResult = DiffUtil.calculateDiff(diffCallback)
|
||||
|
||||
this.items.run {
|
||||
clear()
|
||||
addAll(items)
|
||||
diffResult.dispatchUpdatesTo(this@LiveRoomProfileListAdapter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
ItemLiveRoomProfileBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: LiveRoomProfileListAdapter.ViewHolder, position: Int) {
|
||||
holder.bind(items[position])
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.count()
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
package kr.co.vividnext.sodalive.live.room.profile
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.LiveData
|
||||
import coil.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomUserProfileBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
|
||||
class LiveRoomUserProfileDialog(
|
||||
private val activity: FragmentActivity,
|
||||
private val userProfileLiveData: LiveData<GetLiveRoomUserProfileResponse>,
|
||||
layoutInflater: LayoutInflater,
|
||||
private val isStaff: (Long) -> Boolean,
|
||||
private val onClickSendMessage: (Long, String) -> Unit,
|
||||
private val onClickSetManager: (Long) -> Unit,
|
||||
private val onClickReleaseManager: (Long) -> Unit,
|
||||
private val onClickFollow: (Long) -> Unit,
|
||||
private val onClickUnFollow: (Long) -> Unit,
|
||||
private val onClickInviteSpeaker: (Long) -> Unit,
|
||||
private val onClickChangeListener: (Long) -> Unit,
|
||||
private val onClickKickOut: (Long) -> Unit,
|
||||
private val onClickPopupMenu: (Long, String, Boolean, View) -> Unit,
|
||||
) {
|
||||
private val alertDialog: AlertDialog
|
||||
private val dialogView = DialogLiveRoomUserProfileBinding.inflate(layoutInflater)
|
||||
|
||||
private var isIntroduceFold = true
|
||||
|
||||
init {
|
||||
val dialogBuilder = AlertDialog.Builder(activity)
|
||||
dialogBuilder.setView(dialogView.root)
|
||||
|
||||
alertDialog = dialogBuilder.create()
|
||||
alertDialog.setCancelable(false)
|
||||
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
|
||||
setupView()
|
||||
bindUserProfile()
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
|
||||
fun show() {
|
||||
alertDialog.show()
|
||||
|
||||
val lp = WindowManager.LayoutParams()
|
||||
lp.copyFrom(alertDialog.window?.attributes)
|
||||
lp.width = activity.resources.displayMetrics.widthPixels - (40.dpToPx()).toInt()
|
||||
lp.height = activity.resources.displayMetrics.heightPixels - (37.3f.dpToPx()).toInt()
|
||||
|
||||
alertDialog.window?.attributes = lp
|
||||
}
|
||||
|
||||
fun isShowing(): Boolean {
|
||||
return alertDialog.isShowing
|
||||
}
|
||||
|
||||
private fun setupView() {
|
||||
val profileLP = dialogView.ivProfile.layoutParams
|
||||
profileLP.width = activity.resources.displayMetrics.widthPixels - (66.7f.dpToPx()).toInt()
|
||||
profileLP.height = activity.resources.displayMetrics.widthPixels - (66.7f.dpToPx()).toInt()
|
||||
dialogView.ivProfile.layoutParams = profileLP
|
||||
}
|
||||
|
||||
private fun bindUserProfile() {
|
||||
userProfileLiveData.observe(activity) { userProfile ->
|
||||
dialogView.ivProfile.load(userProfile.profileUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(RoundedCornersTransformation(8.dpToPx()))
|
||||
}
|
||||
|
||||
dialogView.tvNickname.text = userProfile.nickname
|
||||
dialogView.tvTags.text = userProfile.tags
|
||||
dialogView.tvGender.text = userProfile.gender
|
||||
dialogView.tvIntroduce.text = userProfile.introduce
|
||||
|
||||
dialogView.ivClose.setOnClickListener { alertDialog.dismiss() }
|
||||
dialogView.llSendMessage.setOnClickListener {
|
||||
onClickSendMessage(
|
||||
userProfile.userId,
|
||||
userProfile.nickname
|
||||
)
|
||||
}
|
||||
|
||||
dialogView.tvIntroduce.setOnClickListener {
|
||||
isIntroduceFold = !isIntroduceFold
|
||||
|
||||
if (isIntroduceFold) {
|
||||
dialogView.tvIntroduce.maxLines = 2
|
||||
} else {
|
||||
dialogView.tvIntroduce.maxLines = Int.MAX_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
if (userProfile.isSpeaker != null) {
|
||||
dialogView.tvInviteSpeaker.visibility = View.VISIBLE
|
||||
|
||||
if (userProfile.isSpeaker) {
|
||||
dialogView.tvInviteSpeaker.text = "리스너 변경"
|
||||
dialogView.tvInviteSpeaker.setOnClickListener {
|
||||
onClickChangeListener(userProfile.userId)
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
} else {
|
||||
dialogView.tvInviteSpeaker.text = "스피커 초대"
|
||||
dialogView.tvInviteSpeaker.setOnClickListener {
|
||||
onClickInviteSpeaker(userProfile.userId)
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dialogView.tvInviteSpeaker.visibility = View.GONE
|
||||
dialogView.tvKickOut.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (userProfile.isManager != null) {
|
||||
dialogView.tvSetManager.visibility = View.VISIBLE
|
||||
|
||||
if (userProfile.isManager) {
|
||||
dialogView.tvSetManager.text = "스탭 해제"
|
||||
dialogView.tvSetManager.setOnClickListener {
|
||||
onClickReleaseManager(userProfile.userId)
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
} else {
|
||||
dialogView.tvSetManager.text = "스탭 지정"
|
||||
dialogView.tvSetManager.setOnClickListener {
|
||||
onClickSetManager(userProfile.userId)
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dialogView.tvSetManager.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (
|
||||
(userProfile.isSpeaker != null && !isStaff(userProfile.userId)) ||
|
||||
(userProfile.isSpeaker != null && userProfile.isManager != null)
|
||||
) {
|
||||
dialogView.tvKickOut.visibility = View.VISIBLE
|
||||
dialogView.tvKickOut.setOnClickListener {
|
||||
onClickKickOut(userProfile.userId)
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
if (userProfile.isFollowing != null) {
|
||||
dialogView.llFollow.visibility = View.VISIBLE
|
||||
|
||||
if (userProfile.isFollowing) {
|
||||
dialogView.tvFollow.text = "팔로잉"
|
||||
dialogView.ivFollow.setImageResource(R.drawable.ic_alarm_selected)
|
||||
dialogView.llFollow.setBackgroundResource(
|
||||
R.drawable.bg_round_corner_23_3_3e1b93_9970ff
|
||||
)
|
||||
dialogView.tvFollow.setOnClickListener { onClickUnFollow(userProfile.userId) }
|
||||
} else {
|
||||
dialogView.tvFollow.text = "팔로우"
|
||||
dialogView.ivFollow.setImageResource(R.drawable.ic_alarm)
|
||||
dialogView.llFollow.setBackgroundResource(
|
||||
R.drawable.bg_round_corner_23_3_transparent_9970ff
|
||||
)
|
||||
dialogView.tvFollow.setOnClickListener { onClickFollow(userProfile.userId) }
|
||||
}
|
||||
} else {
|
||||
dialogView.llFollow.visibility = View.GONE
|
||||
}
|
||||
|
||||
dialogView.ivMenu.setOnClickListener {
|
||||
onClickPopupMenu(
|
||||
userProfile.userId,
|
||||
userProfile.nickname,
|
||||
userProfile.isBlock,
|
||||
dialogView.ivMenu
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package kr.co.vividnext.sodalive.live.room.update
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.net.Uri
|
||||
import android.view.LayoutInflater
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import coil.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomInfoUpdateBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
|
||||
class LiveRoomInfoEditDialog(
|
||||
activity: Activity,
|
||||
layoutInflater: LayoutInflater,
|
||||
onClickImagePicker: () -> Unit
|
||||
) {
|
||||
private val alertDialog: AlertDialog
|
||||
private val dialogView = DialogLiveRoomInfoUpdateBinding.inflate(layoutInflater)
|
||||
|
||||
private var coverImageUrl: String? = null
|
||||
private var coverImageUri: Uri? = null
|
||||
|
||||
init {
|
||||
val dialogBuilder = AlertDialog.Builder(activity)
|
||||
dialogBuilder.setView(dialogView.root)
|
||||
|
||||
alertDialog = dialogBuilder.create()
|
||||
alertDialog.setCancelable(false)
|
||||
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
|
||||
dialogView.ivPhotoPicker.setOnClickListener { onClickImagePicker() }
|
||||
dialogView.ivClose.setOnClickListener { alertDialog.dismiss() }
|
||||
dialogView.tvCancel.setOnClickListener { alertDialog.dismiss() }
|
||||
}
|
||||
|
||||
fun setRoomInfo(
|
||||
currentTitle: String,
|
||||
currentContent: String,
|
||||
) {
|
||||
dialogView.etTitle.setText(currentTitle)
|
||||
dialogView.etContent.setText(currentContent)
|
||||
}
|
||||
|
||||
fun setCoverImageUri(coverImageUri: Uri) {
|
||||
this.coverImageUri = coverImageUri
|
||||
dialogView.ivCover.load(coverImageUri) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
|
||||
}
|
||||
}
|
||||
|
||||
fun setCoverImageUrl(coverImageUrl: String) {
|
||||
this.coverImageUrl = coverImageUrl
|
||||
dialogView.ivCover.load(coverImageUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
|
||||
}
|
||||
}
|
||||
|
||||
fun setConfirmAction(confirmAction: (String, String, Uri?) -> Unit) {
|
||||
dialogView.tvConfirm.setOnClickListener {
|
||||
alertDialog.dismiss()
|
||||
|
||||
val newTitle = dialogView.etTitle.text.toString()
|
||||
val newContent = dialogView.etContent.text.toString()
|
||||
confirmAction(newTitle, newContent, coverImageUri)
|
||||
coverImageUri = null
|
||||
coverImageUrl = null
|
||||
}
|
||||
}
|
||||
|
||||
fun show(width: Int) {
|
||||
alertDialog.show()
|
||||
|
||||
val lp = WindowManager.LayoutParams()
|
||||
lp.copyFrom(alertDialog.window?.attributes)
|
||||
lp.width = width - (26.7f.dpToPx()).toInt()
|
||||
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
|
||||
alertDialog.window?.attributes = lp
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package kr.co.vividnext.sodalive.report
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import kr.co.vividnext.sodalive.databinding.DialogProfileReportBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
|
||||
class ProfileReportDialog(
|
||||
activity: Activity,
|
||||
layoutInflater: LayoutInflater,
|
||||
confirmButtonClick: () -> Unit
|
||||
) {
|
||||
|
||||
private val alertDialog: AlertDialog
|
||||
|
||||
val dialogView = DialogProfileReportBinding.inflate(layoutInflater)
|
||||
|
||||
init {
|
||||
val dialogBuilder = AlertDialog.Builder(activity)
|
||||
dialogBuilder.setView(dialogView.root)
|
||||
|
||||
alertDialog = dialogBuilder.create()
|
||||
alertDialog.setCancelable(false)
|
||||
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
|
||||
dialogView.tvCancel.setOnClickListener {
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
|
||||
dialogView.tvReport.setOnClickListener {
|
||||
alertDialog.dismiss()
|
||||
confirmButtonClick()
|
||||
}
|
||||
}
|
||||
|
||||
fun show(width: Int) {
|
||||
alertDialog.show()
|
||||
|
||||
val lp = WindowManager.LayoutParams()
|
||||
lp.copyFrom(alertDialog.window?.attributes)
|
||||
lp.width = width - (26.7f.dpToPx()).toInt()
|
||||
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
|
||||
alertDialog.window?.attributes = lp
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package kr.co.vividnext.sodalive.report
|
||||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.POST
|
||||
|
||||
interface ReportApi {
|
||||
@POST("/report")
|
||||
fun report(
|
||||
@Body request: ReportRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package kr.co.vividnext.sodalive.report
|
||||
|
||||
class ReportRepository(private val api: ReportApi) {
|
||||
fun report(request: ReportRequest, token: String) = api.report(request, authHeader = token)
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package kr.co.vividnext.sodalive.report
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class ReportRequest(
|
||||
@SerializedName("type") val type: ReportType,
|
||||
@SerializedName("reason") val reason: String,
|
||||
@SerializedName("reportedAccountId") val reportedAccountId: Long? = null,
|
||||
@SerializedName("cheersId") val cheersId: Long? = null,
|
||||
@SerializedName("audioContentId") val audioContentId: Long? = null,
|
||||
)
|
||||
|
||||
enum class ReportType {
|
||||
@SerializedName("REVIEW") REVIEW,
|
||||
@SerializedName("PROFILE") PROFILE,
|
||||
@SerializedName("USER") USER,
|
||||
@SerializedName("CHEERS") CHEERS,
|
||||
@SerializedName("AUDIO_CONTENT") AUDIO_CONTENT
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package kr.co.vividnext.sodalive.report
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.WindowManager
|
||||
import android.widget.RadioButton
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import kr.co.vividnext.sodalive.databinding.DialogUserReportBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
|
||||
class UserReportDialog(
|
||||
activity: Activity,
|
||||
layoutInflater: LayoutInflater,
|
||||
confirmButtonClick: (String) -> Unit
|
||||
) {
|
||||
|
||||
private val alertDialog: AlertDialog
|
||||
|
||||
val dialogView = DialogUserReportBinding.inflate(layoutInflater)
|
||||
|
||||
var reason = ""
|
||||
|
||||
init {
|
||||
val dialogBuilder = AlertDialog.Builder(activity)
|
||||
dialogBuilder.setView(dialogView.root)
|
||||
|
||||
alertDialog = dialogBuilder.create()
|
||||
alertDialog.setCancelable(false)
|
||||
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
|
||||
dialogView.tvCancel.setOnClickListener {
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
|
||||
dialogView.tvReport.setOnClickListener {
|
||||
alertDialog.dismiss()
|
||||
confirmButtonClick(reason)
|
||||
}
|
||||
|
||||
dialogView.radioGroup.setOnCheckedChangeListener { radioGroup, checkedId ->
|
||||
val radioButton = radioGroup.findViewById<RadioButton>(checkedId)
|
||||
reason = radioButton.text.toString()
|
||||
}
|
||||
}
|
||||
|
||||
fun show(width: Int) {
|
||||
alertDialog.show()
|
||||
|
||||
val lp = WindowManager.LayoutParams()
|
||||
lp.copyFrom(alertDialog.window?.attributes)
|
||||
lp.width = width - (26.7f.dpToPx()).toInt()
|
||||
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
|
||||
alertDialog.window?.attributes = lp
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package kr.co.vividnext.sodalive.user
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class CreatorFollowRequestRequest(@SerializedName("creatorId") val creatorId: Long)
|
|
@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.user
|
|||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest
|
||||
import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest
|
||||
import kr.co.vividnext.sodalive.mypage.MyPageResponse
|
||||
import kr.co.vividnext.sodalive.settings.notification.GetMemberInfoResponse
|
||||
|
@ -56,4 +57,28 @@ interface UserApi {
|
|||
@Query("container") container: String = "aos",
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<MyPageResponse>>
|
||||
|
||||
@POST("/member/block")
|
||||
fun memberBlock(
|
||||
@Body request: MemberBlockRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@POST("/member/unblock")
|
||||
fun memberUnBlock(
|
||||
@Body request: MemberBlockRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@POST("/member/creator/follow")
|
||||
fun creatorFollow(
|
||||
request: Any,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@POST("/member/creator/unfollow")
|
||||
fun creatorUnFollow(
|
||||
request: Any,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.user
|
|||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest
|
||||
import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest
|
||||
import kr.co.vividnext.sodalive.mypage.MyPageResponse
|
||||
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
|
||||
|
@ -35,4 +36,14 @@ class UserRepository(private val userApi: UserApi) {
|
|||
fun getMyPage(token: String): Single<ApiResponse<MyPageResponse>> {
|
||||
return userApi.getMyPage(authHeader = token)
|
||||
}
|
||||
|
||||
fun memberBlock(
|
||||
userId: Long,
|
||||
token: String
|
||||
) = userApi.memberBlock(request = MemberBlockRequest(userId), authHeader = token)
|
||||
|
||||
fun memberUnBlock(
|
||||
userId: Long,
|
||||
token: String
|
||||
) = userApi.memberUnBlock(request = MemberBlockRequest(userId), authHeader = token)
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 978 B |
After Width: | Height: | Size: 781 B |
After Width: | Height: | Size: 804 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 401 B |
After Width: | Height: | Size: 634 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 891 B |
After Width: | Height: | Size: 491 B |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 787 B |
After Width: | Height: | Size: 645 B |
After Width: | Height: | Size: 564 B |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 198 B |
After Width: | Height: | Size: 950 B |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 23 KiB |
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_222222" />
|
||||
<corners
|
||||
android:bottomLeftRadius="10dp"
|
||||
android:bottomRightRadius="10dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_222222" />
|
||||
</shape>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_2b2635" />
|
||||
<corners
|
||||
android:bottomLeftRadius="4.7dp"
|
||||
android:bottomRightRadius="4.7dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_2b2635" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:endColor="@color/color_4999e3"
|
||||
android:gradientRadius="50%"
|
||||
android:startColor="@color/color_4999e3"
|
||||
android:type="radial" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:gradientRadius="50%"
|
||||
android:endColor="@color/color_6f3dec"
|
||||
android:startColor="@color/color_9970ff"
|
||||
android:type="radial" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:gradientRadius="50%"
|
||||
android:endColor="@color/color_bbbbbb"
|
||||
android:startColor="@color/color_9f9f9f"
|
||||
android:type="radial" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:gradientRadius="50%"
|
||||
android:endColor="@color/color_dcdcdc"
|
||||
android:startColor="@color/color_9f9f9f"
|
||||
android:type="radial" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:gradientRadius="50%"
|
||||
android:endColor="@color/color_c67e4a"
|
||||
android:startColor="@color/color_e5a578"
|
||||
android:type="radial" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:gradientRadius="50%"
|
||||
android:endColor="@color/color_c67e4a"
|
||||
android:startColor="@color/color_e6a77a"
|
||||
android:type="radial" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:gradientRadius="50%"
|
||||
android:endColor="@color/color_fdca2f"
|
||||
android:startColor="@color/color_ffdc00"
|
||||
android:type="radial" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:gradientRadius="50%"
|
||||
android:endColor="@color/color_ffb600"
|
||||
android:startColor="@color/color_ffdc00"
|
||||
android:type="radial" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:gradientRadius="50%"
|
||||
android:endColor="@color/white"
|
||||
android:startColor="@color/color_9f9f9f"
|
||||
android:type="radial" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_232323" />
|
||||
<corners android:radius="10dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_eeeeee" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_99525252" />
|
||||
<corners android:radius="10dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_99525252" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_33ffffff" />
|
||||
<corners android:radius="13.3dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/white" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/white" />
|
||||
<corners android:radius="13.3dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/white" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<corners android:radius="13.3dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_9970ff" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<corners android:radius="13.3dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_bbbbbb" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<corners android:radius="13.3dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_ff5c49" />
|
||||
</shape>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<corners android:radius="15dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_bbbbbb" />
|
||||
</shape>
|