diff --git a/app/src/main/java/io/agora/flat/common/rtc/AgoraRtc.kt b/app/src/main/java/io/agora/flat/common/rtc/AgoraRtc.kt index 8d84ed55..72e65b72 100644 --- a/app/src/main/java/io/agora/flat/common/rtc/AgoraRtc.kt +++ b/app/src/main/java/io/agora/flat/common/rtc/AgoraRtc.kt @@ -5,6 +5,7 @@ import io.agora.flat.data.AppEnv import io.agora.flat.di.interfaces.Logger import io.agora.flat.di.interfaces.RtcApi import io.agora.flat.di.interfaces.StartupInitializer +import io.agora.rtc.Constants import io.agora.rtc.IRtcEngineEventHandler import io.agora.rtc.RtcEngine import io.agora.rtc.models.ChannelMediaOptions @@ -106,6 +107,16 @@ class AgoraRtc @Inject constructor(val appEnv: AppEnv, val logger: Logger) : Rtc } trySend(RtcEvent.VolumeIndication(info, totalVolume)) } + + override fun onNetworkQuality(uid: Int, txQuality: Int, rxQuality: Int) { + if (uid == 0) { + trySend(RtcEvent.NetworkStatus(getOverallQuality(txQuality, rxQuality))) + } + } + + override fun onRtcStats(stats: IRtcEngineEventHandler.RtcStats) { + trySend(RtcEvent.LastmileDelay(stats.lastmileDelay)) + } } mHandler.addListener(listener) awaitClose { @@ -113,4 +124,15 @@ class AgoraRtc @Inject constructor(val appEnv: AppEnv, val logger: Logger) : Rtc mHandler.removeListener(listener) } } + + companion object { + internal fun getOverallQuality(txQuality: Int, rxQuality: Int): NetworkQuality { + return when (maxOf(txQuality, rxQuality)) { + Constants.QUALITY_UNKNOWN -> NetworkQuality.Unknown + Constants.QUALITY_EXCELLENT -> NetworkQuality.Excellent + Constants.QUALITY_GOOD -> NetworkQuality.Good + else -> NetworkQuality.Bad + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/io/agora/flat/common/rtc/Misc.kt b/app/src/main/java/io/agora/flat/common/rtc/Misc.kt index e3479a23..390944ec 100644 --- a/app/src/main/java/io/agora/flat/common/rtc/Misc.kt +++ b/app/src/main/java/io/agora/flat/common/rtc/Misc.kt @@ -105,11 +105,11 @@ internal interface RTCEventListener { fun onLastmileQuality(quality: Int) {} - fun onLastmileProbeResult(result: IRtcEngineEventHandler.LastmileProbeResult?) {} + fun onLastmileProbeResult(result: IRtcEngineEventHandler.LastmileProbeResult) {} - fun onLocalVideoStats(stats: IRtcEngineEventHandler.LocalVideoStats?) {} + fun onLocalVideoStats(stats: IRtcEngineEventHandler.LocalVideoStats) {} - fun onRtcStats(stats: IRtcEngineEventHandler.RtcStats?) {} + fun onRtcStats(stats: IRtcEngineEventHandler.RtcStats) {} fun onNetworkQuality(uid: Int, txQuality: Int, rxQuality: Int) {} diff --git a/app/src/main/java/io/agora/flat/common/rtc/RtcEvent.kt b/app/src/main/java/io/agora/flat/common/rtc/RtcEvent.kt index 58d64de1..99d5e251 100644 --- a/app/src/main/java/io/agora/flat/common/rtc/RtcEvent.kt +++ b/app/src/main/java/io/agora/flat/common/rtc/RtcEvent.kt @@ -6,10 +6,23 @@ data class AudioVolumeInfo( val vad: Int, ) + +enum class NetworkQuality { + Excellent, + Good, + Bad, + Unknown +} + sealed class RtcEvent { data class UserOffline(val uid: Int, val reason: Int) : RtcEvent() data class UserJoined(val uid: Int, val elapsed: Int) : RtcEvent() class VolumeIndication(val speakers: List, val totalVolume: Int) : RtcEvent() + + class NetworkStatus(val quality: NetworkQuality) : RtcEvent() + + class LastmileDelay(val delay: Int) : RtcEvent() + } \ No newline at end of file diff --git a/app/src/main/java/io/agora/flat/ui/activity/play/ClassRoomViewModel.kt b/app/src/main/java/io/agora/flat/ui/activity/play/ClassRoomViewModel.kt index 4e6e6241..026e8004 100644 --- a/app/src/main/java/io/agora/flat/ui/activity/play/ClassRoomViewModel.kt +++ b/app/src/main/java/io/agora/flat/ui/activity/play/ClassRoomViewModel.kt @@ -71,6 +71,8 @@ class ClassRoomViewModel @Inject constructor( val classroomEvent get() = eventbus.events.filterIsInstance() + val rtcEvent get() = rtcApi.observeRtcEvent() + val teacher = userManager.observeUsers().map { it.firstOrNull { user -> user.isOwner } } val students = userManager.observeUsers().map { it.filter { user -> !user.isOwner && (user.isJoined || user.isOnStage) } diff --git a/app/src/main/java/io/agora/flat/ui/activity/play/ExtComponent.kt b/app/src/main/java/io/agora/flat/ui/activity/play/ExtComponent.kt index 9933739f..eb6d5de2 100644 --- a/app/src/main/java/io/agora/flat/ui/activity/play/ExtComponent.kt +++ b/app/src/main/java/io/agora/flat/ui/activity/play/ExtComponent.kt @@ -4,6 +4,7 @@ import android.os.Build import android.os.Bundle import android.widget.FrameLayout import androidx.activity.viewModels +import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope @@ -14,6 +15,8 @@ import coil.load import io.agora.flat.Constants import io.agora.flat.R import io.agora.flat.common.error.FlatErrorHandler +import io.agora.flat.common.rtc.NetworkQuality +import io.agora.flat.common.rtc.RtcEvent import io.agora.flat.data.model.RoomStatus import io.agora.flat.databinding.ComponentExtensionBinding import io.agora.flat.databinding.ComponentRoomStateBinding @@ -27,6 +30,7 @@ import io.agora.flat.util.delayAndFinish import io.agora.flat.util.isDarkMode import io.agora.flat.util.showToast import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.launch /** * display common loading, toast, dialog, global layout change. @@ -98,6 +102,34 @@ class ExtComponent( } } } + + lifecycleScope.launchWhenResumed { + classRoomViewModel.rtcEvent.collect { event -> + when (event) { + is RtcEvent.NetworkStatus -> { + when (event.quality) { + NetworkQuality.Unknown, NetworkQuality.Excellent -> { + roomStateBinding.networkStateIcon.setColorFilter(ContextCompat.getColor(activity, R.color.flat_green_6)) + } + NetworkQuality.Good -> { + roomStateBinding.networkStateIcon.setColorFilter(ContextCompat.getColor(activity, R.color.flat_yellow_6)) + } + NetworkQuality.Bad-> { + roomStateBinding.networkStateIcon.setColorFilter(ContextCompat.getColor(activity, R.color.flat_red_6)) + } + } + } + + is RtcEvent.LastmileDelay -> { + roomStateBinding.networkDelay.text = activity.getString(R.string.room_class_network_delay, event.delay) + } + + else -> { + + } + } + } + } } private fun handleErrorMessage(error: UiMessage) { diff --git a/app/src/main/java/io/agora/flat/ui/activity/play/RtcComponent.kt b/app/src/main/java/io/agora/flat/ui/activity/play/RtcComponent.kt index 44b68fe1..90c3f383 100644 --- a/app/src/main/java/io/agora/flat/ui/activity/play/RtcComponent.kt +++ b/app/src/main/java/io/agora/flat/ui/activity/play/RtcComponent.kt @@ -193,6 +193,9 @@ class RtcComponent( is RtcEvent.VolumeIndication -> { adapter.updateVolume(event.speakers) } + else -> { + + } } } } diff --git a/app/src/main/res/drawable/ic_network_status.xml b/app/src/main/res/drawable/ic_network_status.xml new file mode 100644 index 00000000..50411a15 --- /dev/null +++ b/app/src/main/res/drawable/ic_network_status.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/component_room_state.xml b/app/src/main/res/layout/component_room_state.xml index 56c6a3bb..27958de8 100644 --- a/app/src/main/res/layout/component_room_state.xml +++ b/app/src/main/res/layout/component_room_state.xml @@ -14,6 +14,48 @@ android:layout_width="16dp" android:layout_height="0dp" /> + + + + + + + + + + + + + + + 开始上课: %1$s 剩余时间: %1$s 房间已经结束 + + 网络: + 延迟: %1$dms 云盘 已使用 %1$s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bc85d8b6..0602b49f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -242,6 +242,9 @@ Time Left: %1$s Ended + Network: + Latency: %1$dms + Cloud Storage %1$s used Photos diff --git a/app/src/test/java/io/agora/flat/common/rtc/AgoraRtcTest.kt b/app/src/test/java/io/agora/flat/common/rtc/AgoraRtcTest.kt new file mode 100644 index 00000000..7213cb74 --- /dev/null +++ b/app/src/test/java/io/agora/flat/common/rtc/AgoraRtcTest.kt @@ -0,0 +1,18 @@ +package io.agora.flat.common.rtc + +import org.junit.Test + +class AgoraRtcTest { + @Test + fun getOverallQuality() { + assert(AgoraRtc.getOverallQuality(0, 0) == NetworkQuality.Unknown) + assert(AgoraRtc.getOverallQuality(1, 0) == NetworkQuality.Excellent) + assert(AgoraRtc.getOverallQuality(2, 0) == NetworkQuality.Good) + assert(AgoraRtc.getOverallQuality(3, 0) == NetworkQuality.Bad) + assert(AgoraRtc.getOverallQuality(0, 1) == NetworkQuality.Excellent) + assert(AgoraRtc.getOverallQuality(0, 2) == NetworkQuality.Good) + assert(AgoraRtc.getOverallQuality(0, 3) == NetworkQuality.Bad) + assert(AgoraRtc.getOverallQuality(1, 2) == NetworkQuality.Good) + assert(AgoraRtc.getOverallQuality(2, 3) == NetworkQuality.Bad) + } +} \ No newline at end of file