需要添加直播接口

This commit is contained in:
cbb
2026-01-12 17:52:15 +08:00
parent 83fec2617c
commit 13af9eb303
281 changed files with 313157 additions and 104 deletions

View File

@@ -0,0 +1,14 @@
{
"minSdkVersion": "21",
"dependencies": [
"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3",
"com.google.code.gson:gson:2.10.1",
"com.github.yyued:SVGAPlayer-Android:2.6.1",
"androidx.constraintlayout:constraintlayout:2.1.4",
"com.google.android.material:material:1.11.0",
"com.tencent.liteav:LiteAVSDK_Professional:12.7+",
"com.tencent.imsdk:imsdk-plus:8.7.+",
"com.tencent.liteav.tuikit:tuicore:8.7.+",
"io.trtc.uikit:common:3.3.+"
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
package uts.sdk.modules.atomicx.kotlin
import android.graphics.BitmapFactory
import android.text.TextUtils
import com.google.gson.Gson
import com.tencent.cloud.tuikit.engine.room.TUIRoomDefine
import com.tencent.cloud.tuikit.engine.room.TUIRoomEngine
private const val TAG = "UTS-CallExperimentalApi: "
object ExperimentalApiInvoker {
private val gson = Gson()
// const data = { "api": "setTestEnvironment", "params": { "enableRoomTestEnv": true } } // 设置 IM 测试环境
// const data = { "api": "setLocalVideoMuteImage", "params": { "image": "filePath" } } // 设置垫片
// const giftData = { "api": "setCurrentLanguage", "params": { "language" : "en"} } // 礼物功能设置语言
public fun callExperimentalAPI(
jsonString: String,
callback: TUIRoomDefine.ExperimentalAPIResponseCallback?,
) {
val requestData: RequestData = gson.fromJson(jsonString, RequestData::class.java)
if (requestData.api == "setLocalVideoMuteImage") {
setLocalVideoMuteImage(requestData, callback)
return
}
TUIRoomEngine.sharedInstance().callExperimentalAPI(jsonString) { jsonData ->
// Logger.i(TAG + "${requestData.api}: onResponse: $jsonData")
callback?.onResponse(jsonData)
}
}
private fun setLocalVideoMuteImage(
data: RequestData,
callback: TUIRoomDefine.ExperimentalAPIResponseCallback?,
) {
try {
val filePath = data.params?.image
if (TextUtils.isEmpty(filePath)) {
// Logger.e(TAG + "setLocalVideoMuteImage: filePath is empty")
callback?.onResponse("setLocalVideoMuteImage: filePath is empty")
return
}
val bitmap = BitmapFactory.decodeFile(filePath)
TUIRoomEngine.sharedInstance().setLocalVideoMuteImage(bitmap)
} catch (e: Exception) {
// Logger.e(TAG + "setLocalVideoMuteImage: ${e.message}")
callback?.onResponse("setLocalVideoMuteImage: unexpected error")
}
}
}
data class RequestData(
val api: String,
val params: Params?,
)
// 不要修改数据,每个数据对应一个关键字
data class Params(
val image: String?,
)

View File

@@ -0,0 +1,82 @@
package uts.sdk.modules.atomicx.kotlin
import android.content.Context
import android.graphics.Outline
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.ViewOutlineProvider
import androidx.constraintlayout.widget.ConstraintLayout
import io.dcloud.uts.console
import io.trtc.tuikit.atomicxcore.api.view.CoreViewType
import io.trtc.tuikit.atomicxcore.api.view.LiveCoreView
private const val TAG = "UTS-LiveRenderView: "
class LiveRenderView(context: Context, attrs: AttributeSet? = null) : ConstraintLayout(context, attrs) {
private var cornerRadius: Float = 48f // 圆角半径
private var nativeViewType = CoreViewType.PLAY_VIEW
init {
clipToOutline = true
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setRoundRect(0, 0, view.width, view.height, cornerRadius)
}
}
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
outlineProvider.getOutline(this, Outline())
invalidateOutline()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
Log.w(TAG, "onAttachedToWindow")
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
Log.w(TAG, "onDetachedFromWindow")
}
public fun updateViewType(viewType: Any) {
if (viewType == null) {
return
}
if (viewType !is String) {
return
}
if (viewType == "PUSH_VIEW") {
nativeViewType = CoreViewType.PUSH_VIEW
}
}
public fun updateRenderView(liveID: Any) {
console.warn("StreamView, updateRenderView liveID: ", liveID, nativeViewType)
Logger.i(TAG + "updateRenderView: liveID:" + liveID + ", viewType: " + nativeViewType)
if (liveID == null) {
console.error("StreamView, updateRenderView liveID is invalid")
Logger.e(TAG + "updateRenderView: liveID is invalid")
return
}
if (liveID !is String) {
console.error("StreamView, updateRenderView liveID is not String")
Logger.e(TAG + "updateRenderView: liveID is not String")
return
}
if(liveID.isEmpty()) {
console.error("StreamView, updateRenderView liveID is empty")
Logger.e(TAG + "updateRenderView: liveID is empty")
return
}
removeAllViews()
val renderView = LiveCoreView(context, null, 0, nativeViewType)
renderView.setLiveId(liveID)
val lp = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
addView(renderView, lp)
}
}

View File

@@ -0,0 +1,79 @@
package uts.sdk.modules.atomicx.kotlin
import android.util.Log
import com.tencent.liteav.base.ContextUtils
import com.tencent.trtc.TRTCCloud
import org.json.JSONException
import org.json.JSONObject
class Logger {
companion object {
private const val API = "TuikitLog"
private const val LOG_KEY_API = "api"
private const val LOG_KEY_PARAMS = "params"
private const val LOG_KEY_PARAMS_LEVEL = "level"
private const val LOG_KEY_PARAMS_MESSAGE = "message"
private const val LOG_KEY_PARAMS_FILE = "file"
private const val LOG_KEY_PARAMS_LINE = "line"
private const val LOG_KEY_PARAMS_MODULE = "module"
private const val LOG_VALUE_PARAMS_MODULE = "call_state"
private const val LOG_LEVEL_INFO = 0
private const val LOG_LEVEL_WARNING = 1
private const val LOG_LEVEL_ERROR = 2
private const val LOG_FUNCTION_CALLER_INDEX = 5
fun i(message: String) {
log(LOG_LEVEL_INFO, message)
}
fun w(message: String) {
log(LOG_LEVEL_WARNING, message)
}
fun e(message: String) {
log(LOG_LEVEL_ERROR, message)
}
private fun log(
level: Int,
message: String,
) {
var context = ContextUtils.getApplicationContext()
if (context == null) {
ContextUtils.initContextFromNative("liteav")
context = ContextUtils.getApplicationContext()
}
if (context == null) {
return
}
try {
val paramsJson = JSONObject()
paramsJson.put(LOG_KEY_PARAMS_LEVEL, level)
paramsJson.put(LOG_KEY_PARAMS_MESSAGE, message)
paramsJson.put(LOG_KEY_PARAMS_MODULE, LOG_VALUE_PARAMS_MODULE)
paramsJson.put(LOG_KEY_PARAMS_FILE, getCallerFileName())
paramsJson.put(LOG_KEY_PARAMS_LINE, getCallerLineNumber())
val loggerJson = JSONObject()
loggerJson.put(LOG_KEY_API, API)
loggerJson.put(LOG_KEY_PARAMS, paramsJson)
TRTCCloud.sharedInstance(context).callExperimentalAPI(loggerJson.toString())
} catch (e: JSONException) {
Log.e("Logger", e.toString())
}
}
private fun getCallerFileName(): String {
val stackTrace = Thread.currentThread().stackTrace
if (stackTrace.size < LOG_FUNCTION_CALLER_INDEX + 1) return ""
return stackTrace[LOG_FUNCTION_CALLER_INDEX].fileName
}
private fun getCallerLineNumber(): Int {
val stackTrace = Thread.currentThread().stackTrace
if (stackTrace.size < LOG_FUNCTION_CALLER_INDEX + 1) return 0
return stackTrace[LOG_FUNCTION_CALLER_INDEX].lineNumber
}
}
}

View File

@@ -0,0 +1,141 @@
package uts.sdk.modules.atomicx.kotlin
import android.content.Context
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import com.opensource.svgaplayer.SVGACallback
import com.opensource.svgaplayer.SVGAImageView
import com.opensource.svgaplayer.SVGAParser
import com.opensource.svgaplayer.SVGAVideoEntity
import io.dcloud.uts.console
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.InputStream
import java.net.URL
class SVGAAnimationView(context: Context) : FrameLayout(context), SVGACallback {
private val svgaParser: SVGAParser
private val svgaImageView: SVGAImageView
private var svgaCallback: SVGACallback? = null
init {
svgaImageView = SVGAImageView(context)
val lp: FrameLayout.LayoutParams =
FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
addView(svgaImageView, lp)
svgaImageView.loops = 1
svgaImageView.callback = this
svgaParser = SVGAParser.shareParser()
svgaParser.init(context)
}
fun setCallback(callback: SVGACallback) {
svgaCallback = callback
}
// val playUrl = "/sdcard/Android/data/uni.app.UNIFA697C8/images/sports_car.svga"
fun startAnimation(playUrl: String) {
console.log("startAnimation playUrl: ", playUrl)
// Logger.e(TAG + "startAnimation, playUrl: $playUrl")
if (playUrl.isNullOrEmpty()) {
console.error("startAnimation, playUrl is empty")
// Logger.e(TAG + "startAnimation, playUrl is empty")
svgaCallback?.onFinished()
return
}
if (playUrl.endsWith(".svga") && isUrl(playUrl)) {
decodeFromURL(playUrl)
} else {
decodeFromInputStream(playUrl)
}
}
private fun isUrl(url: String): Boolean = url.startsWith("http") || url.startsWith("https")
fun stopAnimation() {
svgaImageView.stopAnimation(true)
}
private fun decodeFromURL(playUrl: String) {
console.log("decodeFromURL, playUrl: ", playUrl)
svgaParser.decodeFromURL(URL(playUrl), object : SVGAParser.ParseCompletion {
override fun onComplete(videoItem: SVGAVideoEntity) {
console.log("decodeFromURL onComplete, videoItem: ", videoItem)
// Logger.i(TAG + "startAnimation decodeFromURL, videoItem: $videoItem")
svgaImageView.setVisibility(View.VISIBLE)
svgaImageView.setVideoItem(videoItem)
svgaImageView.startAnimation()
}
override fun onError() {
console.log("===== decodeFromURL failed")
// Logger.e(TAG + "decodeFromURL failed, playUrl: $playUrl")
svgaCallback?.onFinished()
}
},)
}
private fun decodeFromInputStream(filePath: String) {
console.log("decodeFromInputStream, filePath: ", filePath)
val stream = openInputStream(filePath)
if (stream == null) {
console.log("decodeFromInputStream, filePath is null")
// Logger.e(TAG + "decodeFromInputStream failed, filePath is null")
return
}
svgaParser.decodeFromInputStream(stream, "", object : SVGAParser.ParseCompletion {
override fun onComplete(videoItem: SVGAVideoEntity) {
console.log("======startAnimation decodeFromInputStream start: ", videoItem)
// Logger.i(TAG + "decodeFromInputStream start: videoItem: $videoItem")
svgaImageView.setVisibility(VISIBLE)
svgaImageView.setVideoItem(videoItem)
svgaImageView.startAnimation()
}
override fun onError() {
console.log("======decodeFromInputStream parse failed: ", filePath)
// Logger.e(TAG + "decodeFromInputStream parse failed, filePath: $filePath")
svgaCallback?.onFinished()
}
}, true, null, "", )
}
override fun onFinished() {
console.log("SVGAAnimationView onFinished")
// Logger.i(TAG + "onFinished")
svgaImageView.setVisibility(View.GONE)
svgaCallback?.onFinished()
}
override fun onPause() {
}
override fun onRepeat() {
}
override fun onStep(frame: Int, percentage: Double) {
}
private fun openInputStream(path: String): InputStream? {
try {
val file = File(path)
if (file.exists()) {
return FileInputStream(file)
}
} catch (e: FileNotFoundException) {
Log.i(TAG, " " + e.localizedMessage)
}
return null
}
companion object {
private const val TAG = "UTS-SVGAAnimationView: "
}
}

View File

@@ -0,0 +1,42 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.device.AudioEffectStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object AudioEffectStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun audioEffectStoreChanged(callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
AudioEffectStore.shared().audioEffectState.isEarMonitorOpened
.collect { enable ->
callback("isEarMonitorOpened", gson.toJson(enable))
}
}
launch {
AudioEffectStore.shared().audioEffectState.earMonitorVolume
.collect { volume ->
callback("earMonitorVolume", gson.toJson(volume))
}
}
launch {
AudioEffectStore.shared().audioEffectState.audioChangerType
.collect { type ->
callback("audioChangerType", gson.toJson(type.value))
}
}
launch {
AudioEffectStore.shared().audioEffectState.audioReverbType
.collect { type ->
callback("audioReverbType", gson.toJson(type.value))
}
}
}
}
}

View File

@@ -0,0 +1,32 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.barrage.Barrage
import io.trtc.tuikit.atomicxcore.api.barrage.BarrageStore
import io.trtc.tuikit.atomicxcore.api.barrage.BarrageType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object BarrageStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun barrageStoreChanged(liveID: String, callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
BarrageStore.create(liveID).barrageState.messageList.collect { messageList ->
callback("messageList", gson.toJson(messageList))
}
}
// TODO: 底层未实现,暂时隐藏
// launch {
// BarrageStore.create(liveID).barrageState.allowSendMessage.collect { allowSendMessage ->
// callback("allowSendMessage", gson.toJson(allowSendMessage))
// }
// }
}
}
}

View File

@@ -0,0 +1,34 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.device.BaseBeautyStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object BaseBeautyStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun beautyStoreChanged(callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
BaseBeautyStore.shared().baseBeautyState.smoothLevel.collect { level ->
callback("smoothLevel", gson.toJson(level))
}
}
launch {
BaseBeautyStore.shared().baseBeautyState.whitenessLevel.collect { level ->
callback("whitenessLevel", gson.toJson(level))
}
}
launch {
BaseBeautyStore.shared().baseBeautyState.ruddyLevel.collect { level ->
callback("ruddyLevel", gson.toJson(level))
}
}
}
}
}

View File

@@ -0,0 +1,36 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.live.BattleStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object BattleStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun battleStoreChanged(liveID: String, callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
BattleStore.create(liveID).battleState.currentBattleInfo.collect { currentBattleInfo ->
callback("currentBattleInfo", gson.toJson(currentBattleInfo))
}
}
launch {
BattleStore.create(liveID).battleState.battleUsers.collect { battleUsers ->
callback("battleUsers", gson.toJson(battleUsers))
}
}
launch {
BattleStore.create(liveID).battleState.battleScore.collect { battleScore ->
callback("battleScore", gson.toJson(battleScore))
}
}
}
}
}

View File

@@ -0,0 +1,72 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.live.CoGuestStore
import io.trtc.tuikit.atomicxcore.api.device.DeviceStatus
import io.trtc.tuikit.atomicxcore.api.live.Role
import io.trtc.tuikit.atomicxcore.api.live.SeatUserInfo
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object CoGuestStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun coGuestStoreChanged(liveID: String, callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
CoGuestStore.create(liveID).coGuestState.connected.collect { connected ->
val list = connected.map { convertSeatInfoToMap(it) }
callback("connected", gson.toJson(list)) // SeatUserInfo
}
}
launch {
CoGuestStore.create(liveID).coGuestState.invitees.collect { invitees ->
callback("invitees", gson.toJson(invitees)) // LiveUserInfo
}
}
launch {
CoGuestStore.create(liveID).coGuestState.applicants.collect { applicants ->
callback("applicants", gson.toJson(applicants)) // LiveUserInfo
}
}
launch {
CoGuestStore.create(liveID).coGuestState.candidates.collect { candidates ->
callback("candidates", gson.toJson(candidates)) // LiveUserInfo
}
}
}
}
private fun convertSeatInfoToMap(info: SeatUserInfo): Map<String, Any> {
val map = mutableMapOf<String, Any>()
map["userID"] = info.userID
map["userName"] = info.userName
map["avatarURL"] = info.avatarURL
map["role"] = info.role
map["liveID"] = info.liveID
map["microphoneStatus"] = convertDeviceStatus(info.microphoneStatus)
map["allowOpenMicrophone"] = info.allowOpenMicrophone
map["cameraStatus"] = convertDeviceStatus(info.cameraStatus)
map["allowOpenCamera"] = info.allowOpenCamera
return map
}
private fun convertDeviceStatus(status: DeviceStatus?): String {
if (status == DeviceStatus.ON) {
return "ON"
}
return "OFF"
}
private fun convertUserRole(role: Role?): String {
return when (role) {
Role.OWNER -> "OWNER"
Role.ADMIN -> "ADMIN"
else -> "GENERAL_USER"
}
}
}

View File

@@ -0,0 +1,45 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.live.CoHostStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object CoHostStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun coHostStoreChanged(liveID: String, callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
CoHostStore.create(liveID).coHostState.coHostStatus.collect { coHostStatus ->
callback("coHostStatus", gson.toJson(coHostStatus))
}
}
launch {
CoHostStore.create(liveID).coHostState.connected.collect { connected ->
callback("connected", gson.toJson(connected))
}
}
// TODO: 底层未实现,暂时隐藏
// launch {
// CoHostStore.create(liveID).coHostState.candidates.collect { candidates ->
// callback("candidates", gson.toJson(candidates))
// }
// }
launch {
CoHostStore.create(liveID).coHostState.invitees.collect { invitees ->
callback("invitees", gson.toJson(invitees))
}
}
launch {
CoHostStore.create(liveID).coHostState.applicant.collect { applicant ->
callback("applicant", gson.toJson(applicant))
}
}
}
}
}

View File

@@ -0,0 +1,87 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.device.DeviceStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object DeviceStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun deviceStoreChanged(callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
DeviceStore.shared().deviceState.microphoneStatus.collect { status ->
callback("microphoneStatus", gson.toJson(status.value))
}
}
launch {
DeviceStore.shared().deviceState.microphoneLastError.collect { deviceError ->
callback("microphoneLastError", gson.toJson(deviceError.value))
}
}
launch {
DeviceStore.shared().deviceState.captureVolume.collect { volume ->
callback("captureVolume", gson.toJson(volume))
}
}
launch {
DeviceStore.shared().deviceState.currentMicVolume.collect { volume ->
callback("currentMicVolume", gson.toJson(volume))
}
}
launch {
DeviceStore.shared().deviceState.outputVolume.collect { volume ->
callback("outputVolume", gson.toJson(volume))
}
}
launch {
DeviceStore.shared().deviceState.cameraStatus.collect { cameraStatus ->
callback("cameraStatus", gson.toJson(cameraStatus.value))
}
}
launch {
DeviceStore.shared().deviceState.cameraLastError.collect { deviceError ->
callback("cameraLastError", gson.toJson(deviceError.value))
}
}
launch {
DeviceStore.shared().deviceState.isFrontCamera.collect { isFrontCamera ->
callback("isFrontCamera", gson.toJson(isFrontCamera))
}
}
launch {
DeviceStore.shared().deviceState.localMirrorType.collect { localMirrorType ->
callback("localMirrorType", gson.toJson(localMirrorType))
}
}
launch {
DeviceStore.shared().deviceState.localVideoQuality.collect { quality ->
callback("localVideoQuality", gson.toJson(quality))
}
}
launch {
DeviceStore.shared().deviceState.currentAudioRoute.collect { audioRoute ->
callback("currentAudioRoute", gson.toJson(audioRoute.value))
}
}
launch {
DeviceStore.shared().deviceState.screenStatus.collect { screenStatus ->
callback("screenStatus", gson.toJson(screenStatus.value))
}
}
launch {
DeviceStore.shared().deviceState.networkInfo.collect { networkInfo ->
callback("networkInfo", gson.toJson(networkInfo))
}
}
}
}
}

View File

@@ -0,0 +1,24 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.gift.GiftStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object GiftStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun giftStoreChanged(liveID: String, callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
GiftStore.create(liveID).giftState.usableGifts.collect { usableGifts ->
callback("usableGifts", gson.toJson(usableGifts))
}
}
}
}
}

View File

@@ -0,0 +1,24 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.live.LikeStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object LikeStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun likeStoreChanged(liveID: String, callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
LikeStore.create(liveID).likeState.totalLikeCount.collect { count ->
callback("totalLikeCount", gson.toJson(count))
}
}
}
}
}

View File

@@ -0,0 +1,31 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.live.LiveAudienceStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object LiveAudienceStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun liveAudienceStoreChanged(liveID: String, callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
LiveAudienceStore.create(liveID).liveAudienceState.audienceList.collect { audienceList ->
callback("audienceList", gson.toJson(audienceList))
}
}
launch {
LiveAudienceStore.create(liveID).liveAudienceState.audienceCount.collect { audienceCount ->
callback("audienceCount", gson.toJson(audienceCount))
}
}
}
}
}

View File

@@ -0,0 +1,35 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.live.LiveListStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object LiveListStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun liveStoreChanged(callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
LiveListStore.shared().liveState.liveList.collect { liveList ->
callback("liveList", gson.toJson(liveList))
}
}
launch {
LiveListStore.shared().liveState.liveListCursor.collect { cursor ->
callback("liveListCursor", gson.toJson(cursor))
}
}
launch {
LiveListStore.shared().liveState.currentLive.collect { liveInfo ->
callback("currentLive", gson.toJson(liveInfo))
}
}
}
}
}

View File

@@ -0,0 +1,39 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.live.LiveSeatStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import uts.sdk.modules.atomicx.kotlin.Logger
import io.dcloud.uts.console
object LiveSeatStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun liveSeatStoreChanged(liveID: String, callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
LiveSeatStore.create(liveID).liveSeatState.seatList.collect { seatList ->
val list = gson.toJson(seatList)
console.info("UTS-Live: liveSeatStoreChanged, seatList: ", list)
Logger.i("UTS-Live: " + "liveSeatStoreChanged, seatList: "+ list);
callback("seatList", gson.toJson(seatList))
}
}
launch {
LiveSeatStore.create(liveID).liveSeatState.canvas.collect { canvas ->
callback("canvas", gson.toJson(canvas))
}
}
launch {
LiveSeatStore.create(liveID).liveSeatState.speakingUsers.collect { speakingUsers ->
callback("speakingUsers", gson.toJson(speakingUsers))
}
}
}
}
}

View File

@@ -0,0 +1,23 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import io.trtc.tuikit.atomicxcore.api.live.LiveSummaryStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object LiveSummaryStoreObserver {
private val gson = Gson()
private var bindDataJob: Job? = null
fun liveSummaryStoreChanged(liveID: String, callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
LiveSummaryStore.create(liveID).liveSummaryState.summaryData.collect { data ->
callback("summaryData", gson.toJson(data))
}
}
}
}
}

View File

@@ -0,0 +1,32 @@
package uts.sdk.modules.atomicx.observer
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import io.trtc.tuikit.atomicxcore.api.login.LoginStatus
import io.trtc.tuikit.atomicxcore.api.login.LoginStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object LoginStoreObserver {
private val gson = GsonBuilder().serializeNulls().create()
private var bindDataJob: Job? = null
fun loginStoreChanged(callback: (String, String) -> Unit) {
bindDataJob?.cancel()
bindDataJob = CoroutineScope(Dispatchers.Main).launch {
launch {
LoginStore.shared.loginState.loginUserInfo.collect { userInfo ->
callback("loginUserInfo", gson.toJson(userInfo))
}
}
launch {
LoginStore.shared.loginState.loginStatus.collect { loginStatus ->
// UNLOGIN \ LOGINED
callback("loginStatus", gson.toJson(loginStatus))
}
}
}
}
}

View File

@@ -0,0 +1,693 @@
import { ILiveListener } from '../../interface';
import { GiftListener, Gift, } from 'io.trtc.tuikit.atomicxcore.api.gift';
import {
LiveUserInfo, LikeListener, LiveAudienceListener, LiveListListener,
LiveEndedReason, LiveKickedOutReason, LiveSeatListener, DeviceControlPolicy,
HostListener, GuestListener, NoResponseReason, CoHostListener, SeatUserInfo,
BattleListener, BattleInfo, BattleEndedReason,
} from 'io.trtc.tuikit.atomicxcore.api.live';
const TAG = "UTS-Event"
export const giftListenerMap = new Map<string, Array<ILiveListener>>();
export const likeListenerMap = new Map<string, Array<ILiveListener>>();
export const audienceListenerMap = new Map<string, Array<ILiveListener>>();
export const liveListListenerMap = new Map<string, Array<ILiveListener>>();
export const liveSeatListenerMap = new Map<string, Array<ILiveListener>>();
export const coGuestHostListenerMap = new Map<string, Array<ILiveListener>>();
export const coGuestGuestListenerMap = new Map<string, Array<ILiveListener>>();
export const coHostListenerMap = new Map<string, Array<ILiveListener>>();
export const battleListenerMap = new Map<string, Array<ILiveListener>>();
export type NativeLiveListener = {
listener : (eventName : string, data : string) => void
}
// 创建LiveList事件分发器
export function createLiveListEventDispatcher() {
const liveListener : NativeLiveListener = {
listener: (eventName : string, data : string) => {
liveListListenerMap.get(eventName)?.forEach((iLiveListener) => {
iLiveListener.callback(data);
});
},
};
return liveListener
}
// 创建 LiveAudience 事件分发器
export function createAudienceEventDispatcher() {
const liveListener : NativeLiveListener = {
listener: (eventName : string, data : string) => {
audienceListenerMap.get(eventName)?.forEach((iLiveListener) => {
iLiveListener.callback(data);
});
},
};
return liveListener
}
// 创建CoHost事件分发器
export function createCoHostEventDispatcher() {
const liveListener : NativeLiveListener = {
listener: (eventName : string, data : string) => {
coHostListenerMap.get(eventName)?.forEach((iLiveListener) => {
iLiveListener.callback(data);
});
},
};
return liveListener
}
// 创建CoGuestHost事件分发器
export function createCoGuestHostEventDispatcher() {
const liveListener : NativeLiveListener = {
listener: (eventName : string, data : string) => {
coGuestHostListenerMap.get(eventName)?.forEach((iLiveListener) => {
iLiveListener.callback(data);
});
},
};
return liveListener
}
// 创建CoGuestGuest事件分发器
export function createCoGuestGuestEventDispatcher() {
const liveListener : NativeLiveListener = {
listener: (eventName : string, data : string) => {
coGuestGuestListenerMap.get(eventName)?.forEach((iLiveListener) => {
iLiveListener.callback(data);
});
},
};
return liveListener
}
// 创建Gift事件分发器
export function createGiftEventDispatcher() {
const liveListener : NativeLiveListener = {
listener: (eventName : string, data : string) => {
giftListenerMap.get(eventName)?.forEach((iLiveListener) => {
iLiveListener.callback(data);
});
},
};
return liveListener
}
// 创建Like事件分发器
export function createLikeEventDispatcher() {
const liveListener : NativeLiveListener = {
listener: (eventName : string, data : string) => {
likeListenerMap.get(eventName)?.forEach((iLiveListener) => {
iLiveListener.callback(data);
});
},
};
return liveListener
}
// 创建Battle事件分发器
export function createBattleEventDispatcher() {
const liveListener : NativeLiveListener = {
listener: (eventName : string, data : string) => {
battleListenerMap.get(eventName)?.forEach((iLiveListener) => {
iLiveListener.callback(data);
});
},
};
return liveListener
}
// 创建LiveSeat事件分发器
export function createLiveSeatEventDispatcher() {
const liveListener : NativeLiveListener = {
listener: (eventName : string, data : string) => {
liveSeatListenerMap.get(eventName)?.forEach((iLiveListener) => {
iLiveListener.callback(data);
});
},
};
return liveListener
}
export class TGiftListener extends GiftListener {
private listener : (eventType : string, data : string) => void;
constructor(options : NativeLiveListener) {
super();
this.listener = options.listener;
}
override onReceiveGift(liveID : String, gift : Gift, count : Int, sender : LiveUserInfo) {
console.log(`${TAG} onReceiveGift liveID: ${liveID},gift:${gift} ,count: ${count}, sender: ${sender}`);
const giftData = {
giftID: gift.giftID,
name: gift.name,
desc: gift.desc,
iconURL: gift.iconURL,
resourceURL: gift.resourceURL,
level: gift.level,
coins: gift.coins,
extensionInfo: gift.extensionInfo
};
const senderUser = {
userID: sender.userID,
userName: sender.userName,
avatarURL: sender.avatarURL
}
let data = {
liveID,
gift: giftData,
count,
sender: senderUser
}
this.listener('onReceiveGift', JSON.stringify(data));
}
}
export class TLikeListener extends LikeListener {
private listener : (eventType : string, data : string) => void;
constructor(options : NativeLiveListener) {
super();
this.listener = options.listener;
}
override onReceiveLikesMessage(liveID : String, totalLikesReceived : Long, sender : LiveUserInfo) {
console.log(`${TAG} onReceiveLikesMessage liveID: ${liveID}, totalLikesReceived: ${totalLikesReceived}, sender: ${sender}`);
const senderUser = {
userID: sender.userID,
userName: sender.userName,
avatarURL: sender.avatarURL
};
let data = {
liveID,
totalLikesReceived,
sender: senderUser
};
this.listener('onReceiveLikesMessage', JSON.stringify(data));
}
}
export class TLiveAudienceListener extends LiveAudienceListener {
private listener : (eventType : string, data : string) => void;
constructor(options : NativeLiveListener) {
super();
this.listener = options.listener;
}
override onAudienceJoined(audience : LiveUserInfo) {
console.log(`${TAG} onAudienceJoined audience: ${audience}`);
const audienceUser = {
userID: audience.userID,
userName: audience.userName,
avatarURL: audience.avatarURL
};
let data = {
audience: audienceUser
}
this.listener('onAudienceJoined', JSON.stringify(data));
}
override onAudienceLeft(audience : LiveUserInfo) {
console.log(`${TAG} onAudienceLeft audience: ${audience}`);
const audienceUser = {
userID: audience.userID,
userName: audience.userName,
avatarURL: audience.avatarURL
};
let data = {
audience: audienceUser
}
this.listener('onAudienceLeft', JSON.stringify(data));
}
override onAudienceMessageDisabled(audience : LiveUserInfo, isDisable : Boolean) {
console.log(`${TAG} onAudienceMessageDisabled audience: ${audience}`);
const audienceUser = {
userID: audience.userID,
userName: audience.userName,
avatarURL: audience.avatarURL
};
let data = {
audience: audienceUser,
isDisable
}
this.listener('onAudienceMessageDisabled', JSON.stringify(data));
}
}
export class TLiveListListener extends LiveListListener {
private listener : (eventType : string, data : string) => void;
constructor(options : NativeLiveListener) {
super();
this.listener = options.listener;
}
override onLiveEnded(liveID : String, reason : LiveEndedReason, message : String) {
let reasonStr = "ENDED_BY_HOST"
if (reason == LiveEndedReason.ENDED_BY_SERVER) {
reasonStr = "ENDED_BY_SERVER"
}
let data = {
liveID,
reason: reasonStr,
message
};
console.log(`${TAG} onLiveEnded, data : ${JSON.stringify(data)}`)
this.listener('onLiveEnded', JSON.stringify(data));
}
override onKickedOutOfLive(liveID : String, reason : LiveKickedOutReason, message : String) {
let data = {
liveID,
reason: this.convertKickedOutReasonToString(reason),
message
};
console.log(`${TAG} onKickedOutOfLive, data : ${JSON.stringify(data)}`);
this.listener('onKickedOutOfLive', JSON.stringify(data));
}
private convertKickedOutReasonToString(reason : LiveKickedOutReason) : String {
switch (reason) {
case LiveKickedOutReason.BY_LOGGED_ON_OTHER_DEVICE:
return "BY_LOGGED_ON_OTHER_DEVICE"
case LiveKickedOutReason.BY_SERVER:
return "BY_SERVER"
case LiveKickedOutReason.FOR_NETWORK_DISCONNECTED:
return "FOR_NETWORK_DISCONNECTED"
case LiveKickedOutReason.FOR_JOIN_ROOM_STATUS_INVALID_DURING_OFFLINE:
return "FOR_JOIN_ROOM_STATUS_INVALID_DURING_OFFLINE"
case LiveKickedOutReason.FOR_COUNT_OF_JOINED_ROOMS_EXCEED_LIMIT:
return "FOR_COUNT_OF_JOINED_ROOMS_EXCEED_LIMIT"
default:
return "BY_ADMIN"
}
}
}
export class TLiveSeatListener extends LiveSeatListener {
private listener : (eventType : string, data : string) => void;
constructor(options : NativeLiveListener) {
super();
this.listener = options.listener;
}
override onLocalCameraOpenedByAdmin(policy : DeviceControlPolicy) {
console.log(`${TAG} onLocalCameraOpenedByAdmin policy: ${policy}`);
let devicePolicy = "FORCE_OPEN"
if (policy == DeviceControlPolicy.UNLOCK_ONLY) {
devicePolicy = "UNLOCK_ONLY"
}
this.listener('onLocalCameraOpenedByAdmin', devicePolicy);
}
override onLocalCameraClosedByAdmin() {
console.log(`${TAG} onLocalCameraClosedByAdmin`);
this.listener('onLocalCameraClosedByAdmin', "");
}
override onLocalMicrophoneOpenedByAdmin(policy : DeviceControlPolicy) {
console.log(`${TAG} onLocalMicrophoneOpenedByAdmin policy: ${policy}`);
let devicePolicy = "FORCE_OPEN"
if (policy == DeviceControlPolicy.UNLOCK_ONLY) {
devicePolicy = "UNLOCK_ONLY"
}
this.listener('onLocalMicrophoneOpenedByAdmin', devicePolicy);
}
override onLocalMicrophoneClosedByAdmin() {
console.log(`${TAG} onLocalMicrophoneClosedByAdmin`);
this.listener('onLocalMicrophoneClosedByAdmin', "");
}
}
// CoGuestStore: HostListener
export class TCoGuestHostListener extends HostListener {
private listener : (eventType : string, data : string) => void;
constructor(options : NativeLiveListener) {
super();
this.listener = options.listener;
}
override onGuestApplicationReceived(guestUser : LiveUserInfo) {
console.log(`${TAG} onGuestApplicationReceived guestUser: ${guestUser}`);
const guestUserInfo = {
userID: guestUser.userID,
userName: guestUser.userName,
avatarURL: guestUser.avatarURL
};
let data = {
guestUser: guestUserInfo,
};
this.listener('onGuestApplicationReceived', JSON.stringify(data));
}
override onGuestApplicationCancelled(guestUser : LiveUserInfo) {
console.log(`${TAG} onGuestApplicationCancelled guestUser: ${guestUser}`);
const guestUserInfo = {
userID: guestUser.userID,
userName: guestUser.userName,
avatarURL: guestUser.avatarURL
};
let data = {
guestUser: guestUserInfo,
};
this.listener('onGuestApplicationCancelled', JSON.stringify(data));
}
override onGuestApplicationProcessedByOtherHost(guestUser : LiveUserInfo, hostUser : LiveUserInfo) {
console.log(`${TAG} onGuestApplicationProcessedByOtherHost guestUser: ${guestUser}, hostUser: ${hostUser}`);
const guestUserInfo = {
userID: guestUser.userID,
userName: guestUser.userName,
avatarURL: guestUser.avatarURL
};
const hostUserInfo = {
userID: hostUser.userID,
userName: hostUser.userName,
avatarURL: hostUser.avatarURL
};
let data = {
guestUser: guestUserInfo,
hostUser: hostUserInfo
};
this.listener('onGuestApplicationProcessedByOtherHost', JSON.stringify(data));
}
override onHostInvitationResponded(isAccept : Boolean, guestUser : LiveUserInfo) {
console.log(`${TAG} onHostInvitationResponded isAccept: ${isAccept}, guestUser: ${guestUser}`);
const guestUserInfo = {
userID: guestUser.userID,
userName: guestUser.userName,
avatarURL: guestUser.avatarURL
};
let data = {
isAccept,
guestUser: guestUserInfo
};
this.listener('onHostInvitationResponded', JSON.stringify(data));
}
override onHostInvitationNoResponse(guestUser : LiveUserInfo, reason : NoResponseReason) {
console.log(`${TAG} onHostInvitationNoResponse guestUser: ${guestUser}, reason: ${reason}`);
const guestUserInfo = {
userID: guestUser.userID,
userName: guestUser.userName,
avatarURL: guestUser.avatarURL
};
let data = {
guestUser: guestUserInfo,
reason: reason
};
this.listener('onHostInvitationNoResponse', JSON.stringify(data));
}
}
// CoGuestStore: GuestListener
export class TCoGuestGuestListener extends GuestListener {
private listener : (eventType : string, data : string) => void;
constructor(options : NativeLiveListener) {
super();
this.listener = options.listener;
}
override onHostInvitationReceived(hostUser : LiveUserInfo) {
const hostUserInfo = {
userID: hostUser.userID,
userName: hostUser.userName,
avatarURL: hostUser.avatarURL
};
let data = {
hostUser: hostUserInfo,
};
console.log(`${TAG} onHostInvitationReceived hostUser: ${JSON.stringify(data)}`);
this.listener('onHostInvitationReceived', JSON.stringify(data));
}
override onHostInvitationCancelled(hostUser : LiveUserInfo) {
const hostUserInfo = {
userID: hostUser.userID,
userName: hostUser.userName,
avatarURL: hostUser.avatarURL
};
let data = {
hostUser: hostUserInfo,
};
console.log(`${TAG} onHostInvitationCancelled hostUser: ${JSON.stringify(data)}`);
this.listener('onHostInvitationCancelled', JSON.stringify(data));
}
override onGuestApplicationResponded(isAccept : Boolean, hostUser : LiveUserInfo) {
const hostUserInfo = {
userID: hostUser.userID,
userName: hostUser.userName,
avatarURL: hostUser.avatarURL
};
let data = {
isAccept,
hostUser: hostUserInfo
};
console.log(`${TAG} onGuestApplicationResponded isAccept: ${isAccept}, hostUser: ${JSON.stringify(data)}`);
this.listener('onGuestApplicationResponded', JSON.stringify(data));
}
override onGuestApplicationNoResponse(reason : NoResponseReason) {
let reasonStr = "TIMEOUT"
if (reason == NoResponseReason.ALREADY_SEATED) {
reasonStr = "ALREADY_SEATED"
}
let data = {
reason: reasonStr,
}
console.log(`${TAG} onGuestApplicationNoResponse reason: ${JSON.stringify(data)}`);
this.listener('onGuestApplicationNoResponse', JSON.stringify(data));
}
override onKickedOffSeat(seatIndex : Int, hostUser : LiveUserInfo) {
console.log(`${TAG} onKickedOffSeat seatIndex: ${seatIndex}, hostUser: ${hostUser}`);
const hostUserInfo = {
userID: hostUser.userID,
userName: hostUser.userName,
avatarURL: hostUser.avatarURL
};
let data = {
seatIndex,
hostUser: hostUserInfo
};
this.listener('onKickedOffSeat', JSON.stringify(data));
}
}
//CoHostStore: CoHostListener
export class TCoHostListener extends CoHostListener {
private listener : (eventType : string, data : string) => void;
constructor(options : NativeLiveListener) {
super();
this.listener = options.listener;
}
private convertSeatUserInfoToData(seatUserInfo ?: SeatUserInfo) {
return {
userID: seatUserInfo?.userID,
userName: seatUserInfo?.userName,
avatarURL: seatUserInfo?.avatarURL,
role: seatUserInfo?.role,
liveID: seatUserInfo?.liveID,
microphoneStatus: seatUserInfo?.microphoneStatus,
allowOpenMicrophone: seatUserInfo?.allowOpenMicrophone,
cameraStatus: seatUserInfo?.cameraStatus,
allowOpenCamera: seatUserInfo?.allowOpenCamera
};
}
override onCoHostRequestReceived(inviter : SeatUserInfo, extensionInfo : String) {
console.log(`${TAG} onCoHostRequestReceived inviter: ${inviter}, extensionInfo: ${extensionInfo}`);
let data = {
inviter: this.convertSeatUserInfoToData(inviter),
extensionInfo: extensionInfo ?? ""
};
this.listener('onCoHostRequestReceived', JSON.stringify(data));
}
override onCoHostRequestCancelled(inviter : SeatUserInfo, invitee ?: SeatUserInfo) {
console.log(`${TAG} onCoHostRequestCancelled inviter: ${inviter}, invitee: ${invitee}`);
let data = {
inviter: this.convertSeatUserInfoToData(inviter),
invitee: this.convertSeatUserInfoToData(invitee)
};
this.listener('onCoHostRequestCancelled', JSON.stringify(data));
}
override onCoHostRequestAccepted(invitee : SeatUserInfo) {
console.log(`${TAG} onCoHostRequestAccepted invitee: ${invitee}`);
let data = {
invitee: this.convertSeatUserInfoToData(invitee)
};
this.listener('onCoHostRequestAccepted', JSON.stringify(data));
}
override onCoHostRequestRejected(invitee : SeatUserInfo) {
console.log(`${TAG} onCoHostRequestRejected invitee: ${invitee}`);
let data = {
invitee: this.convertSeatUserInfoToData(invitee)
};
this.listener('onCoHostRequestRejected', JSON.stringify(data));
}
override onCoHostRequestTimeout(inviter : SeatUserInfo, invitee : SeatUserInfo) {
console.log(`${TAG} onCoHostRequestTimeout inviter: ${inviter}`);
let data = {
inviter: this.convertSeatUserInfoToData(inviter),
invitee: this.convertSeatUserInfoToData(invitee),
};
this.listener('onCoHostRequestTimeout', JSON.stringify(data));
}
override onCoHostUserJoined(userInfo : SeatUserInfo) {
console.log(`${TAG} onCoHostUserJoined userInfo: ${userInfo}`);
let data = {
userInfo: this.convertSeatUserInfoToData(userInfo)
}
this.listener('onCoHostUserJoined', JSON.stringify(data));
}
override onCoHostUserLeft(userInfo : SeatUserInfo) {
console.log(`${TAG} onCoHostUserLeft userInfo: ${userInfo}`);
let data = {
userInfo: this.convertSeatUserInfoToData(userInfo)
}
this.listener('onCoHostUserLeft', JSON.stringify(data));
}
}
// BattleStore: BattleListener
export class TBattleListener extends BattleListener {
private listener : (eventType : string, data : string) => void;
constructor(options : NativeLiveListener) {
super();
this.listener = options.listener;
}
private convertSeatUserInfoToData(seatUserInfo ?: SeatUserInfo) {
return {
userID: seatUserInfo?.userID,
userName: seatUserInfo?.userName,
avatarURL: seatUserInfo?.avatarURL,
role: seatUserInfo?.role,
liveID: seatUserInfo?.liveID,
microphoneStatus: seatUserInfo?.microphoneStatus,
allowOpenMicrophone: seatUserInfo?.allowOpenMicrophone,
cameraStatus: seatUserInfo?.cameraStatus,
allowOpenCamera: seatUserInfo?.allowOpenCamera
};
}
private convertBattleInfoToData(battleInfo : BattleInfo) {
let battleConfig = {
duration: battleInfo.config.duration,
needResponse: battleInfo.config.needResponse,
extensionInfo: battleInfo.config.extensionInfo
}
return {
battleID: battleInfo.battleID,
config: battleConfig,
startTime: battleInfo.startTime,
endTime: battleInfo.endTime
};
}
override onBattleStarted(battleInfo : BattleInfo, inviter : SeatUserInfo, invitees : List<SeatUserInfo>) {
console.log(`${TAG} onBattleStarted battleInfo: ${battleInfo}, inviter: ${inviter}, invitees: ${invitees}`);
let data = {
battleInfo: this.convertBattleInfoToData(battleInfo),
inviter: this.convertSeatUserInfoToData(inviter),
invitees: invitees.map(user => this.convertSeatUserInfoToData(user))
};
console.log(`${TAG} onBattleStarted data: ${JSON.stringify(data)}`);
this.listener('onBattleStarted', JSON.stringify(data));
}
override onBattleEnded(battleInfo : BattleInfo, reason ?: BattleEndedReason) {
console.log(`${TAG} onUserJoinBattle battleInfo: ${battleInfo}, reason: ${reason}`);
let reasonStr = "TIME_OVER"
if (reason == BattleEndedReason.ALL_MEMBER_EXIT) {
reasonStr = "ALL_MEMBER_EXIT"
}
let data = {
battleInfo: this.convertBattleInfoToData(battleInfo),
reason: reasonStr,
};
console.log(`${TAG} onBattleEnded data: ${JSON.stringify(data)}`);
this.listener('onBattleEnded', JSON.stringify(data));
}
override onUserJoinBattle(battleID : String, battleUser : SeatUserInfo) {
console.log(`${TAG} onUserJoinBattle battleID: ${battleID}, battleUser: ${battleUser}`);
let data = {
battleID,
battleUser: this.convertSeatUserInfoToData(battleUser)
};
console.log(`${TAG} onUserJoinBattle data: ${JSON.stringify(data)}`);
this.listener('onUserJoinBattle', JSON.stringify(data));
}
override onUserExitBattle(battleID : String, battleUser : SeatUserInfo) {
console.log(`${TAG} onUserExitBattle battleID: ${battleID}, battleUser: ${battleUser}`);
let data = {
battleID,
battleUser: this.convertSeatUserInfoToData(battleUser)
};
console.log(`${TAG} onUserExitBattle data: ${JSON.stringify(data)}`);
this.listener('onUserExitBattle', JSON.stringify(data));
}
override onBattleRequestReceived(battleID : String, inviter : SeatUserInfo, invitee : SeatUserInfo) {
console.log(`${TAG} onBattleRequestReceived battleID: ${battleID}, inviter: ${inviter}, invitee: ${invitee}`);
let data = {
battleID,
inviter: this.convertSeatUserInfoToData(inviter),
invitee: this.convertSeatUserInfoToData(invitee)
};
console.log(`${TAG} onBattleRequestReceived data: ${JSON.stringify(data)}`);
this.listener('onBattleRequestReceived', JSON.stringify(data));
}
override onBattleRequestCancelled(battleID : String, inviter : SeatUserInfo, invitee : SeatUserInfo) {
console.log(`${TAG} onBattleRequestCancelled battleID: ${battleID}, inviter: ${inviter}, invitee: ${invitee}`);
let data = {
battleID,
inviter: this.convertSeatUserInfoToData(inviter),
invitee: this.convertSeatUserInfoToData(invitee)
};
console.log(`${TAG} onBattleRequestCancelled data: ${JSON.stringify(data)}`);
this.listener('onBattleRequestCancelled', JSON.stringify(data));
}
override onBattleRequestTimeout(battleID : String, inviter : SeatUserInfo, invitee : SeatUserInfo) {
console.log(`${TAG} onBattleRequestTimeout battleID: ${battleID}, inviter: ${inviter}, invitee: ${invitee}`);
let data = {
battleID,
inviter: this.convertSeatUserInfoToData(inviter),
invitee: this.convertSeatUserInfoToData(invitee)
};
console.log(`${TAG} onBattleRequestTimeout data: ${JSON.stringify(data)}`);
this.listener('onBattleRequestTimeout', JSON.stringify(data));
}
override onBattleRequestAccept(battleID : String, inviter : SeatUserInfo, invitee : SeatUserInfo) {
console.log(`${TAG} onBattleRequestAccept battleID: ${battleID}, inviter: ${inviter}, invitee: ${invitee}`);
let data = {
battleID,
inviter: this.convertSeatUserInfoToData(inviter),
invitee: this.convertSeatUserInfoToData(invitee)
};
console.log(`${TAG} onBattleRequestAccept data: ${JSON.stringify(data)}`);
this.listener('onBattleRequestAccept', JSON.stringify(data));
}
override onBattleRequestReject(battleID : String, inviter : SeatUserInfo, invitee : SeatUserInfo) {
console.log(`${TAG} onBattleRequestReject battleID: ${battleID}, inviter: ${inviter}, invitee: ${invitee}`);
let data = {
battleID,
inviter: this.convertSeatUserInfoToData(inviter),
invitee: this.convertSeatUserInfoToData(invitee)
};
console.log(`${TAG} onBattleRequestReject data: ${JSON.stringify(data)}`);
this.listener('onBattleRequestReject', JSON.stringify(data));
}
}

View File

@@ -0,0 +1,76 @@
<template>
<view class="defaultStyles"> </view>
</template>
<script lang="uts">
import LiveRenderView from 'uts.sdk.modules.atomicx.kotlin.LiveRenderView';
import Log from "android.util.Log"
const STREAM_TAG = "LiveRenderView"
let liveID : Any = ""
export default {
name: "live-core-view",
props: {
"liveID": {
type: Any,
default: ""
},
"viewType": {
type: String,
default: "PLAY_VIEW"
},
},
watch: {
"liveID": {
handler(newValue : Any, oldValue : Any) {
console.log(`${STREAM_TAG} liveID newValue, ${newValue}`);
Log.e(STREAM_TAG, "liveID newValue, " + newValue)
liveID = newValue
this.$el?.updateRenderView(newValue)
},
immediate: true // 创建时是否通过此方法更新属性默认值为false
},
"viewType": {
handler(newValue : String, oldValue : String) {
console.log(`${STREAM_TAG} liveID newValue, ${newValue}`);
this.$el?.updateViewType(newValue)
},
immediate: true
},
},
created() {
console.log(`${STREAM_TAG} created`);
Log.e(STREAM_TAG, "created ")
},
NVLoad() : LiveRenderView {
let streamView = new LiveRenderView($androidContext!)
streamView.updateRenderView(liveID)
console.log(`${STREAM_TAG} NVLoad, ${streamView}`);
Log.e(STREAM_TAG, "NVLoad ")
return streamView;
},
NVLoaded() {
console.log(`${STREAM_TAG} NVLoaded`);
Log.e(STREAM_TAG, "NVLoaded ")
},
NVLayouted() {
},
NVBeforeUnload() {
},
NVUnloaded() {
console.log(`${STREAM_TAG} NVUnloaded`);
Log.e(STREAM_TAG, "NVUnloaded ")
},
unmounted() {
console.log(`${STREAM_TAG} unmounted`);
Log.e(STREAM_TAG, "unmounted ")
}
}
</script>
<style>
/* 定义默认样式值, 组件使用者没有配置时使用 */
.defaultStyles {
background-color: white;
}
</style>

View File

@@ -0,0 +1,83 @@
<template>
<view class="defaultStyles"> </view>
</template>
<script lang="uts">
import Log from "android.util.Log"
import SVGAAnimationView from "uts.sdk.modules.atomicx.kotlin.SVGAAnimationView";
import SVGACallback from "com.opensource.svgaplayer.SVGACallback"
const SVGA_TAG = "SvgaPlayer"
export default {
name: "svga-player",
/**
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
*/
emits: ['onFinished'],
/**
* 规则如果没有配置expose则 methods 中的方法均对外暴露如果配置了expose则以expose的配置为准向外暴露
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
*/
expose: ['startPlay', 'stopPlay'],
methods: {
startPlay(url : string) {
console.log(`${SVGA_TAG} startAnimation, url: ${url}`);
Log.e(SVGA_TAG, "startAnimation, url: " + url)
this.$el?.startAnimation(url)
},
stopPlay() {
console.log(`${SVGA_TAG} stop`);
Log.e(SVGA_TAG, "stopAnimation ")
this.$el?.stopAnimation()
}
},
created() {
console.log(`${SVGA_TAG} created`);
Log.e(SVGA_TAG, "created ")
},
NVLoad() : SVGAAnimationView {
let svgaView = new SVGAAnimationView($androidContext!)
svgaView.setCallback(new InnerSVGACallback(this))
console.log(`${SVGA_TAG} NVLoad, ${svgaView}`);
Log.e(SVGA_TAG, "NVLoad ")
return svgaView;
},
NVLoaded() {
},
NVLayouted() {
},
NVUnloaded() {
console.log(`${SVGA_TAG} NVUnloaded`);
Log.e(SVGA_TAG, "NVUnloaded ")
},
unmounted() {
console.log(`${SVGA_TAG} unmounted`);
Log.e(SVGA_TAG, "unmounted ")
}
}
class InnerSVGACallback extends SVGACallback {
private comp : UTSComponent<SVGAAnimationView>;
constructor(comp : UTSComponent<SVGAAnimationView>) {
super();
this.comp = comp;
}
override onPause() { }
override onFinished() {
console.log("SvgaPlayer onFinished")
this.comp.$emit("onFinished")
}
override onRepeat() { }
override onStep(frame : Int, percentage : Double) { }
}
</script>
<style>
/* 定义默认样式值, 组件使用者没有配置时使用 */
.defaultStyles {
background-color: white;
}
</style>

View File

@@ -0,0 +1,216 @@
import {
UserProfileParam, LiveInfoParam, TakeSeatModeType, LiveUserInfoParam, MoveSeatPolicyType,
VideoQualityType, BarrageParam, MessageType, AudioReverbTypeParam, AudioChangerTypeParam,
LiveModifyFlag, AppendLocalTipOptions,
} from '../../interface.uts';
import { UserProfile, AllowType, Gender, } from 'io.trtc.tuikit.atomicxcore.api.login';
import { LiveInfo, LiveUserInfo, TakeSeatMode, MoveSeatPolicy, } from 'io.trtc.tuikit.atomicxcore.api.live';
import { VideoQuality, AudioChangerType, AudioReverbType, } from 'io.trtc.tuikit.atomicxcore.api.device';
import { Barrage, BarrageType, } from 'io.trtc.tuikit.atomicxcore.api.barrage';
export class ParamsCovert {
public static convertUserProfile(userProfile : UserProfileParam) : UserProfile {
const nativeUserProfile = new UserProfile()
nativeUserProfile.userID = userProfile.userID ?? ""
nativeUserProfile.nickname = userProfile.nickname
nativeUserProfile.avatarURL = userProfile.avatarURL
nativeUserProfile.selfSignature = userProfile.selfSignature
nativeUserProfile.gender = Gender.parse(userProfile.gender?.toInt() ?? 0)
nativeUserProfile.role = userProfile.role?.toInt()
nativeUserProfile.level = userProfile.level?.toInt()
nativeUserProfile.birthday = userProfile.birthday?.toLong()
nativeUserProfile.allowType = AllowType.parse(userProfile.allowType?.toInt() ?? 0)
//nativeUserProfile.customInfo = userProfile.customInfo //TODO: ByteArray转换
return nativeUserProfile
}
public static convertLiveInfo(liveInfo : LiveInfoParam) : LiveInfo {
const nativeLiveInfo = new LiveInfo()
nativeLiveInfo.liveID = liveInfo.liveID
nativeLiveInfo.liveName = liveInfo.liveName ?? ""
nativeLiveInfo.notice = liveInfo.notice ?? ""
nativeLiveInfo.isMessageDisable = liveInfo.isMessageDisable ?? false
nativeLiveInfo.isPublicVisible = liveInfo.isPublicVisible ?? true
nativeLiveInfo.isSeatEnabled = liveInfo.isSeatEnabled ?? true
nativeLiveInfo.keepOwnerOnSeat = liveInfo.keepOwnerOnSeat ?? false
nativeLiveInfo.maxSeatCount = liveInfo.maxSeatCount?.toInt() ?? 0
nativeLiveInfo.seatMode = ParamsCovert.convertTakeSeatMode(liveInfo.seatMode)
nativeLiveInfo.seatLayoutTemplateID = liveInfo.seatLayoutTemplateID?.toInt() ?? 600
nativeLiveInfo.coverURL = liveInfo.coverURL ?? ""
nativeLiveInfo.backgroundURL = liveInfo.backgroundURL ?? ""
let list = mutableListOf<Int>()
liveInfo.categoryList?.forEach((info : number) => {
list.add(info.toInt())
})
nativeLiveInfo.categoryList = list;
nativeLiveInfo.activityStatus = liveInfo.activityStatus?.toInt() ?? 0
nativeLiveInfo.totalViewerCount = liveInfo.totalViewerCount?.toInt() ?? 0
nativeLiveInfo.isGiftEnabled = liveInfo.isGiftEnabled ?? true
nativeLiveInfo.metaData = liveInfo.metaData ?? Map<string, string>()
return nativeLiveInfo
}
private static convertTakeSeatMode(seatMode ?: TakeSeatModeType) : TakeSeatMode {
if (seatMode == 'FREE') {
return TakeSeatMode.FREE
}
return TakeSeatMode.APPLY
}
private static convertModifyFlag(flag : LiveModifyFlag) : LiveInfo.ModifyFlag {
switch (flag) {
case 'LIVE_NAME':
return LiveInfo.ModifyFlag.LIVE_NAME
case 'NOTICE':
return LiveInfo.ModifyFlag.NOTICE
case 'IS_MESSAGE_DISABLE':
return LiveInfo.ModifyFlag.IS_MESSAGE_DISABLE
case 'IS_PUBLIC_VISIBLE':
return LiveInfo.ModifyFlag.IS_PUBLIC_VISIBLE
case 'SEAT_MODE':
return LiveInfo.ModifyFlag.SEAT_MODE
case 'COVER_URL':
return LiveInfo.ModifyFlag.COVER_URL
case 'BACKGROUND_URL':
return LiveInfo.ModifyFlag.BACKGROUND_URL
case 'CATEGORY_LIST':
return LiveInfo.ModifyFlag.CATEGORY_LIST
case 'ACTIVITY_STATUS':
return LiveInfo.ModifyFlag.ACTIVITY_STATUS
case 'SEAT_LAYOUT_TEMPLATE_ID':
return LiveInfo.ModifyFlag.SEAT_LAYOUT_TEMPLATE_ID
default:
return LiveInfo.ModifyFlag.NONE
}
}
public static convertModifyFlagList(modifyFlagList ?: LiveModifyFlag[]) : List<LiveInfo.ModifyFlag> {
let arrays = Array<LiveInfo.ModifyFlag>()
if (modifyFlagList != null && modifyFlagList.length > 0) {
modifyFlagList.forEach((flag : LiveModifyFlag) => {
const result = ParamsCovert.convertModifyFlag(flag)
arrays.push(result)
})
}
return arrays
}
public static convertMoveSeatPolicy(policyType ?: MoveSeatPolicyType) : MoveSeatPolicy {
switch (policyType) {
case 'FORCE_REPLACE':
return MoveSeatPolicy.FORCE_REPLACE;
case 'SWAP_POSITION':
return MoveSeatPolicy.SWAP_POSITION;
default:
return MoveSeatPolicy.ABORT_WHEN_OCCUPIED;
}
}
public static covertVideoQuality(quality : VideoQualityType) : VideoQuality {
switch (quality) {
case 'VIDEOQUALITY_540P':
return VideoQuality.QUALITY_540P; // 标清540P
case 'VIDEOQUALITY_720P':
return VideoQuality.QUALITY_720P; // 高清720P
case 'VIDEOQUALITY_1080P':
return VideoQuality.QUALITY_1080P; // 超清1080P
default:
return VideoQuality.QUALITY_360P; // value = 1 低清360P
}
}
public static convertBarrage(barrageParam : AppendLocalTipOptions) : Barrage {
const nativeBarrage = new Barrage()
nativeBarrage.liveID = barrageParam.liveID
nativeBarrage.sender = ParamsCovert.convertLiveUserInfo(barrageParam.sender)
nativeBarrage.sequence = barrageParam.sequence?.toLong() ?? 0
nativeBarrage.timestampInSecond = barrageParam.timestampInSecond?.toLong() ?? 0
nativeBarrage.messageType = ParamsCovert.convertMessageType(barrageParam.messageType)
nativeBarrage.textContent = barrageParam.textContent ?? ""
nativeBarrage.extensionInfo = barrageParam.extensionInfo ?? Map<string, string>()
nativeBarrage.businessID = barrageParam.businessID ?? ""
nativeBarrage.data = barrageParam.data ?? ""
return nativeBarrage
}
private static convertLiveUserInfo(info : LiveUserInfoParam) : LiveUserInfo {
let liveUseInfo = new LiveUserInfo()
liveUseInfo.userID = info?.userID ?? ""
liveUseInfo.userName = info?.userName ?? ""
liveUseInfo.avatarURL = info?.avatarURL ?? ""
return liveUseInfo
}
private static convertMessageType(messageType ?: MessageType) : BarrageType {
if (messageType == 'CUSTOM') {
return BarrageType.CUSTOM
}
return BarrageType.TEXT
}
public static convertAudioChangerType(changerType : AudioChangerTypeParam) : AudioChangerType {
switch (changerType) {
case 'CHILD':
return AudioChangerType.CHILD;
case 'LITTLE_GIRL':
return AudioChangerType.LITTLE_GIRL;
case 'MAN':
return AudioChangerType.MAN;
case 'HEAVY_METAL':
return AudioChangerType.HEAVY_METAL;
case 'COLD':
return AudioChangerType.COLD;
case 'FOREIGNER':
return AudioChangerType.FOREIGNER;
case 'TRAPPED_BEAST':
return AudioChangerType.TRAPPED_BEAST;
case 'FATSO':
return AudioChangerType.FATSO;
case 'STRONG_CURRENT':
return AudioChangerType.STRONG_CURRENT;
case 'HEAVY_MACHINERY':
return AudioChangerType.HEAVY_MACHINERY;
case 'ETHEREAL':
return AudioChangerType.ETHEREAL;
default:
return AudioChangerType.NONE;
}
}
public static convertAudioReverbType(reverbType : AudioReverbTypeParam) : AudioReverbType {
switch (reverbType) {
case 'KTV':
return AudioReverbType.KTV;
case 'SMALL_ROOM':
return AudioReverbType.SMALL_ROOM;
case 'AUDITORIUM':
return AudioReverbType.AUDITORIUM;
case 'DEEP':
return AudioReverbType.DEEP;
case 'LOUD':
return AudioReverbType.LOUD;
case 'METALLIC':
return AudioReverbType.METALLIC;
case 'MAGNETIC':
return AudioReverbType.MAGNETIC;
default:
return AudioReverbType.NONE;
}
}
public static convertJsonToUTSJSONObject(jsonData ?: string): UTSJSONObject {
let jsonObject = new UTSJSONObject();
if (jsonData == null) {
return jsonObject;
}
try {
jsonObject = JSON.parse(jsonData) as UTSJSONObject;
} catch (error) {
console.error('JSON parse failed:', error);
}
return jsonObject
}
}