live-room(agora): rtm version 1.5.3 -> 2.2.6
This commit is contained in:
@@ -32,6 +32,33 @@ android {
|
||||
includeInBundle = false
|
||||
}
|
||||
|
||||
packaging {
|
||||
// JNI(.so) 관련
|
||||
jniLibs {
|
||||
// pickFirsts: 충돌 시 첫 파일만 채택
|
||||
pickFirsts += ["**/libaosl.so"]
|
||||
}
|
||||
|
||||
// 일반 리소스(META-INF 등) 관련
|
||||
resources {
|
||||
// pickFirsts: 충돌 시 첫 파일만 채택
|
||||
pickFirsts += [
|
||||
"META-INF/LICENSE.txt",
|
||||
"META-INF/NOTICE*"
|
||||
]
|
||||
|
||||
// 자주 쓰는 제외/병합 예시
|
||||
excludes += [
|
||||
"META-INF/DEPENDENCIES",
|
||||
"META-INF/AL2.0",
|
||||
"META-INF/LGPL2.1"
|
||||
]
|
||||
merges += [
|
||||
"META-INF/services/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "kr.co.vividnext.sodalive"
|
||||
minSdk 23
|
||||
@@ -168,8 +195,8 @@ dependencies {
|
||||
implementation "io.github.bootpay:android:4.4.3"
|
||||
|
||||
// agora
|
||||
implementation "io.agora.rtc:voice-sdk:4.6.0"
|
||||
implementation 'io.agora.rtm:rtm-sdk:1.5.3'
|
||||
implementation "io.agora.rtc:voice-sdk:4.2.6"
|
||||
implementation 'io.agora:agora-rtm:2.2.6'
|
||||
|
||||
// Glide
|
||||
implementation 'com.github.bumptech.glide:glide:5.0.5'
|
||||
|
||||
@@ -6,28 +6,31 @@ import io.agora.rtc2.Constants
|
||||
import io.agora.rtc2.IRtcEngineEventHandler
|
||||
import io.agora.rtc2.RtcEngine
|
||||
import io.agora.rtm.ErrorInfo
|
||||
import io.agora.rtm.GetOnlineUsersOptions
|
||||
import io.agora.rtm.GetOnlineUsersResult
|
||||
import io.agora.rtm.PublishOptions
|
||||
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 io.agora.rtm.RtmConfig
|
||||
import io.agora.rtm.RtmConstants
|
||||
import io.agora.rtm.RtmEventListener
|
||||
import io.agora.rtm.SubscribeOptions
|
||||
import kr.co.vividnext.sodalive.BuildConfig
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomRequestType
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class Agora(
|
||||
private val uid: Long,
|
||||
private val context: Context,
|
||||
private val rtcEventHandler: IRtcEngineEventHandler,
|
||||
private val rtmClientListener: RtmClientListener
|
||||
private val rtmEventListener: RtmEventListener
|
||||
) {
|
||||
// RTM client instance
|
||||
private var rtmClient: RtmClient? = null
|
||||
// 상태 플래그: RTM 로그인 완료 여부
|
||||
private var rtmLoggedIn: Boolean = false
|
||||
|
||||
// RTM channel instance
|
||||
private var rtmChannel: RtmChannel? = null
|
||||
|
||||
private var rtcEngine: RtcEngine? = null
|
||||
// 상태 플래그: RTM 로그인 진행 중 여부
|
||||
private var rtmLoginInProgress: Boolean = false
|
||||
|
||||
init {
|
||||
initAgoraEngine()
|
||||
@@ -42,19 +45,23 @@ class Agora(
|
||||
}
|
||||
}
|
||||
|
||||
fun deInitAgoraEngine() {
|
||||
fun deInitAgoraEngine(rtmEventListener: RtmEventListener) {
|
||||
deInitRtcEngine()
|
||||
deInitRtmChannelAndClient()
|
||||
deInitRtmClient(rtmEventListener)
|
||||
}
|
||||
|
||||
// region RtcEngine
|
||||
private var rtcEngine: RtcEngine? = null
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun initRtcEngine() {
|
||||
Logger.e("initRtcEngine")
|
||||
rtcEngine = RtcEngine.create(
|
||||
context,
|
||||
BuildConfig.AGORA_APP_ID,
|
||||
rtcEventHandler
|
||||
)
|
||||
Logger.e("initRtcEngine - rtcEngine: ${rtcEngine != null}")
|
||||
|
||||
rtcEngine!!.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING)
|
||||
rtcEngine!!.setAudioProfile(
|
||||
@@ -66,6 +73,15 @@ class Agora(
|
||||
}
|
||||
|
||||
fun joinRtcChannel(uid: Int, rtcToken: String, channelName: String) {
|
||||
val state = rtcEngine?.connectionState
|
||||
val isDisconnected = state == null || state == Constants.CONNECTION_STATE_DISCONNECTED
|
||||
|
||||
if (!isDisconnected) {
|
||||
Logger.e("joinRtcChannel - skip (state=$state)")
|
||||
return
|
||||
}
|
||||
|
||||
Logger.e("joinRtcChannel - proceed (state=$state) uid=$uid channel=$channelName")
|
||||
rtcEngine!!.joinChannel(
|
||||
rtcToken,
|
||||
channelName,
|
||||
@@ -90,6 +106,10 @@ class Agora(
|
||||
return rtcEngine!!.connectionState
|
||||
}
|
||||
|
||||
fun isRtmLoggedIn(): Boolean {
|
||||
return rtmLoggedIn
|
||||
}
|
||||
|
||||
fun deInitRtcEngine() {
|
||||
if (rtcEngine != null) {
|
||||
rtcEngine!!.leaveChannel()
|
||||
@@ -103,64 +123,179 @@ class Agora(
|
||||
// endregion
|
||||
|
||||
// region RtmClient
|
||||
private var rtmClient: RtmClient? = null
|
||||
private var roomChannelName: String? = null
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun initRtmClient() {
|
||||
rtmClient = RtmClient.createInstance(
|
||||
context,
|
||||
BuildConfig.AGORA_APP_ID,
|
||||
rtmClientListener
|
||||
)
|
||||
val rtmConfig = RtmConfig.Builder(BuildConfig.AGORA_APP_ID, uid.toString())
|
||||
.eventListener(rtmEventListener)
|
||||
.build()
|
||||
|
||||
rtmClient = RtmClient.create(rtmConfig)
|
||||
}
|
||||
|
||||
fun createRtmChannelAndLogin(
|
||||
uid: String,
|
||||
fun rtmLogin(
|
||||
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()
|
||||
}
|
||||
// 이미 RTM 로그인 및 구독이 완료된 경우 재호출 방지
|
||||
if (rtmLoggedIn && roomChannelName == channelName) {
|
||||
Logger.e("rtmLogin - already logged in and subscribed. skip")
|
||||
return
|
||||
}
|
||||
// 로그인 시도 중이면 재호출 방지
|
||||
if (rtmLoginInProgress) {
|
||||
Logger.e("rtmLogin - already in progress. skip")
|
||||
return
|
||||
}
|
||||
|
||||
override fun onFailure(p0: ErrorInfo?) {
|
||||
roomChannelName = channelName
|
||||
|
||||
fun attemptLogin(attempt: Int) {
|
||||
rtmClient!!.login(
|
||||
rtmToken,
|
||||
object : ResultCallback<Void> {
|
||||
override fun onSuccess(p0: Void?) {
|
||||
Logger.e("rtmClient login - success (attempt=$attempt)")
|
||||
// 로그인 성공 후 두 채널 구독 시도
|
||||
subscribeChannel(rtmChannelJoinSuccess, rtmChannelJoinFail)
|
||||
}
|
||||
|
||||
override fun onFailure(p0: ErrorInfo?) {
|
||||
Logger.e("rtmClient login - fail (attempt=$attempt), ${p0?.errorReason}")
|
||||
if (attempt < 4) {
|
||||
attemptLogin(attempt + 1)
|
||||
} else {
|
||||
rtmLoginInProgress = false
|
||||
rtmChannelJoinFail()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onFailure(p0: ErrorInfo?) {
|
||||
}
|
||||
}
|
||||
)
|
||||
rtmLoginInProgress = true
|
||||
attemptLogin(1)
|
||||
}
|
||||
|
||||
fun inputChat(message: String) {
|
||||
val rtmMessage = rtmClient!!.createMessage()
|
||||
rtmMessage.text = message
|
||||
private fun subscribeChannel(
|
||||
rtmChannelJoinSuccess: () -> Unit,
|
||||
rtmChannelJoinFail: () -> Unit
|
||||
) {
|
||||
val targetRoom = roomChannelName
|
||||
if (targetRoom == null) {
|
||||
Logger.e("subscribeChannel - roomChannelName is null")
|
||||
rtmChannelJoinFail()
|
||||
return
|
||||
}
|
||||
|
||||
rtmChannel!!.sendMessage(
|
||||
rtmMessage,
|
||||
object : ResultCallback<Void?> {
|
||||
override fun onSuccess(p0: Void?) {
|
||||
Logger.e("sendMessage - onSuccess")
|
||||
}
|
||||
var completed = false
|
||||
var roomSubscribed = false
|
||||
var inboxSubscribed = false
|
||||
|
||||
override fun onFailure(p0: ErrorInfo) {
|
||||
Logger.e("sendMessage fail - ${p0.errorCode}")
|
||||
Logger.e("sendMessage fail - ${p0.errorDescription}")
|
||||
}
|
||||
fun completeSuccessIfReady() {
|
||||
if (!completed && roomSubscribed && inboxSubscribed) {
|
||||
completed = true
|
||||
rtmLoggedIn = true
|
||||
rtmLoginInProgress = false
|
||||
Logger.e("RTM subscribe - both channels subscribed")
|
||||
rtmChannelJoinSuccess()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun failOnce(reason: String?) {
|
||||
if (!completed) {
|
||||
completed = true
|
||||
Logger.e("RTM subscribe failed: $reason")
|
||||
rtmChannelJoinFail()
|
||||
}
|
||||
}
|
||||
|
||||
fun subscribeRoom(attempt: Int) {
|
||||
val channelOptions = SubscribeOptions()
|
||||
channelOptions.withMessage = true
|
||||
channelOptions.withPresence = true
|
||||
Logger.e("RTM subscribe(room: $targetRoom) attempt=$attempt")
|
||||
rtmClient!!.subscribe(
|
||||
targetRoom,
|
||||
channelOptions,
|
||||
object : ResultCallback<Void> {
|
||||
override fun onSuccess(responseInfo: Void?) {
|
||||
Logger.e("RTM subscribe(room) success at attempt=$attempt")
|
||||
roomSubscribed = true
|
||||
completeSuccessIfReady()
|
||||
}
|
||||
|
||||
override fun onFailure(errorInfo: ErrorInfo?) {
|
||||
Logger.e("RTM subscribe(room) failure at attempt=$attempt reason=${errorInfo?.errorReason}")
|
||||
if (attempt < 4) {
|
||||
subscribeRoom(attempt + 1)
|
||||
} else {
|
||||
failOnce("room subscribe failed after 3 retries (4 attempts)")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun subscribeInbox(attempt: Int) {
|
||||
val inboxChannel = "inbox_$uid"
|
||||
val inboxChannelOptions = SubscribeOptions()
|
||||
inboxChannelOptions.withMessage = true
|
||||
Logger.e("RTM subscribe(inbox: $inboxChannel) attempt=$attempt")
|
||||
rtmClient!!.subscribe(
|
||||
inboxChannel,
|
||||
inboxChannelOptions,
|
||||
object : ResultCallback<Void> {
|
||||
override fun onSuccess(responseInfo: Void?) {
|
||||
Logger.e("RTM subscribe(inbox) success at attempt=$attempt")
|
||||
inboxSubscribed = true
|
||||
completeSuccessIfReady()
|
||||
}
|
||||
|
||||
override fun onFailure(errorInfo: ErrorInfo?) {
|
||||
Logger.e("RTM subscribe(inbox) failure at attempt=$attempt reason=${errorInfo?.errorReason}")
|
||||
if (attempt < 4) {
|
||||
subscribeInbox(attempt + 1)
|
||||
} else {
|
||||
failOnce("inbox subscribe failed after 3 retries (4 attempts)")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 두 채널 구독을 병렬로 시도
|
||||
subscribeRoom(1)
|
||||
subscribeInbox(1)
|
||||
}
|
||||
|
||||
fun inputChat(message: String, onFailure: () -> Unit) {
|
||||
if (roomChannelName != null) {
|
||||
val options = PublishOptions()
|
||||
options.setChannelType(RtmConstants.RtmChannelType.MESSAGE)
|
||||
rtmClient!!.publish(
|
||||
roomChannelName!!,
|
||||
message,
|
||||
options,
|
||||
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.errorReason}")
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Logger.e("inputChat - roomChannelName is null")
|
||||
onFailure()
|
||||
}
|
||||
}
|
||||
|
||||
fun sendRawMessageToGroup(
|
||||
@@ -168,23 +303,30 @@ class Agora(
|
||||
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()
|
||||
}
|
||||
if (roomChannelName != null) {
|
||||
val options = PublishOptions()
|
||||
options.customType = "ByteArray"
|
||||
rtmClient!!.publish(
|
||||
roomChannelName!!,
|
||||
rawMessage,
|
||||
options,
|
||||
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()
|
||||
override fun onFailure(p0: ErrorInfo) {
|
||||
Logger.e("sendMessage fail - ${p0.errorCode}")
|
||||
Logger.e("sendMessage fail - ${p0.errorReason}")
|
||||
onFailure?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Logger.e("inputChat - roomChannelName is null")
|
||||
onFailure?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
fun sendRawMessageToPeer(
|
||||
@@ -193,34 +335,71 @@ class Agora(
|
||||
rawMessage: ByteArray? = null,
|
||||
onSuccess: () -> Unit
|
||||
) {
|
||||
val option = SendMessageOptions()
|
||||
if (roomChannelName != null) {
|
||||
val message = rawMessage ?: requestType.toString().toByteArray()
|
||||
val options = PublishOptions()
|
||||
options.customType = "ByteArray"
|
||||
rtmClient!!.publish(
|
||||
"inbox_$receiverUid",
|
||||
message,
|
||||
options,
|
||||
object : ResultCallback<Void> {
|
||||
override fun onSuccess(p0: Void?) {
|
||||
Logger.e("sendMessage - onSuccess")
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
val message = rtmClient!!.createMessage()
|
||||
message.rawMessage = rawMessage ?: requestType.toString().toByteArray()
|
||||
override fun onFailure(p0: ErrorInfo) {
|
||||
Logger.e("sendMessage fail - ${p0.errorCode}")
|
||||
Logger.e("sendMessage fail - ${p0.errorReason}")
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Logger.e("inputChat - roomChannelName is null")
|
||||
}
|
||||
}
|
||||
|
||||
rtmClient!!.sendMessageToPeer(
|
||||
receiverUid,
|
||||
message,
|
||||
option,
|
||||
object : ResultCallback<Void?> {
|
||||
override fun onSuccess(aVoid: Void?) {
|
||||
onSuccess()
|
||||
fun deInitRtmClient(rtmEventListener: RtmEventListener) {
|
||||
rtmClient?.removeEventListener(rtmEventListener)
|
||||
rtmClient?.unsubscribe(roomChannelName, object : ResultCallback<Void> {
|
||||
override fun onSuccess(responseInfo: Void?) {
|
||||
Logger.e("RTM unsubscribe - $roomChannelName")
|
||||
roomChannelName = null
|
||||
}
|
||||
|
||||
override fun onFailure(errorInfo: ErrorInfo) {
|
||||
Logger.e("RTM unsubscribe fail - ${errorInfo.errorCode}")
|
||||
Logger.e("RTM unsubscribe fail - ${errorInfo.errorReason}")
|
||||
}
|
||||
})
|
||||
rtmClient?.unsubscribe(
|
||||
"inbox_${SharedPreferenceManager.userId}",
|
||||
object : ResultCallback<Void> {
|
||||
override fun onSuccess(responseInfo: Void?) {
|
||||
Logger.e("RTM unsubscribe - inbox_${SharedPreferenceManager.userId}")
|
||||
}
|
||||
|
||||
override fun onFailure(errorInfo: ErrorInfo) {
|
||||
Logger.e("RTM unsubscribe fail - ${errorInfo.errorCode}")
|
||||
Logger.e("RTM unsubscribe fail - ${errorInfo.errorReason}")
|
||||
}
|
||||
})
|
||||
rtmClient?.logout(object : ResultCallback<Void> {
|
||||
override fun onSuccess(responseInfo: Void?) {
|
||||
Logger.e("RTM logout")
|
||||
rtmClient = null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun rtmChannelIsNull(): Boolean {
|
||||
return rtmChannel == null
|
||||
}
|
||||
override fun onFailure(errorInfo: ErrorInfo) {
|
||||
Logger.e("RTM logout fail - ${errorInfo.errorCode}")
|
||||
Logger.e("RTM logout fail - ${errorInfo.errorReason}")
|
||||
}
|
||||
})
|
||||
// 상태 리셋
|
||||
rtmLoggedIn = false
|
||||
rtmLoginInProgress = false
|
||||
|
||||
fun deInitRtmChannelAndClient() {
|
||||
rtmChannel?.leave(null)
|
||||
rtmChannel?.release()
|
||||
rtmClient?.logout(null)
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -86,7 +86,7 @@
|
||||
android:gravity="center"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="취소"
|
||||
android:textColor="@color/color_9970ff"
|
||||
android:textColor="@color/color_3bb9f1"
|
||||
android:textSize="18.3sp" />
|
||||
|
||||
<TextView
|
||||
|
||||
Reference in New Issue
Block a user