正在显示
11 个修改的文件
包含
109 行增加
和
42 行删除
| @@ -34,7 +34,8 @@ class RTCEngine | @@ -34,7 +34,8 @@ class RTCEngine | ||
| 34 | internal constructor( | 34 | internal constructor( |
| 35 | val client: SignalClient, | 35 | val client: SignalClient, |
| 36 | private val pctFactory: PeerConnectionTransport.Factory, | 36 | private val pctFactory: PeerConnectionTransport.Factory, |
| 37 | - @Named(InjectionNames.DISPATCHER_IO) ioDispatcher: CoroutineDispatcher, | 37 | + @Named(InjectionNames.DISPATCHER_IO) |
| 38 | + private val ioDispatcher: CoroutineDispatcher, | ||
| 38 | ) : SignalClient.Listener, DataChannel.Observer { | 39 | ) : SignalClient.Listener, DataChannel.Observer { |
| 39 | internal var listener: Listener? = null | 40 | internal var listener: Listener? = null |
| 40 | 41 | ||
| @@ -77,8 +78,20 @@ internal constructor( | @@ -77,8 +78,20 @@ internal constructor( | ||
| 77 | 78 | ||
| 78 | private val publisherObserver = PublisherTransportObserver(this, client) | 79 | private val publisherObserver = PublisherTransportObserver(this, client) |
| 79 | private val subscriberObserver = SubscriberTransportObserver(this, client) | 80 | private val subscriberObserver = SubscriberTransportObserver(this, client) |
| 80 | - internal lateinit var publisher: PeerConnectionTransport | ||
| 81 | - internal lateinit var subscriber: PeerConnectionTransport | 81 | + |
| 82 | + private var _publisher: PeerConnectionTransport? = null | ||
| 83 | + internal val publisher: PeerConnectionTransport | ||
| 84 | + get() { | ||
| 85 | + return _publisher | ||
| 86 | + ?: throw UninitializedPropertyAccessException("publisher has not been initialized yet.") | ||
| 87 | + } | ||
| 88 | + private var _subscriber: PeerConnectionTransport? = null | ||
| 89 | + internal val subscriber: PeerConnectionTransport | ||
| 90 | + get() { | ||
| 91 | + return _subscriber | ||
| 92 | + ?: throw UninitializedPropertyAccessException("subscriber has not been initialized yet.") | ||
| 93 | + } | ||
| 94 | + | ||
| 82 | private var reliableDataChannel: DataChannel? = null | 95 | private var reliableDataChannel: DataChannel? = null |
| 83 | private var reliableDataChannelSub: DataChannel? = null | 96 | private var reliableDataChannelSub: DataChannel? = null |
| 84 | private var lossyDataChannel: DataChannel? = null | 97 | private var lossyDataChannel: DataChannel? = null |
| @@ -89,13 +102,15 @@ internal constructor( | @@ -89,13 +102,15 @@ internal constructor( | ||
| 89 | 102 | ||
| 90 | private var hasPublished = false | 103 | private var hasPublished = false |
| 91 | 104 | ||
| 92 | - private val coroutineScope = CloseableCoroutineScope(SupervisorJob() + ioDispatcher) | 105 | + private var coroutineScope = CloseableCoroutineScope(SupervisorJob() + ioDispatcher) |
| 93 | 106 | ||
| 94 | init { | 107 | init { |
| 95 | client.listener = this | 108 | client.listener = this |
| 96 | } | 109 | } |
| 97 | 110 | ||
| 98 | suspend fun join(url: String, token: String, options: ConnectOptions): LivekitRtc.JoinResponse { | 111 | suspend fun join(url: String, token: String, options: ConnectOptions): LivekitRtc.JoinResponse { |
| 112 | + coroutineScope.close() | ||
| 113 | + coroutineScope = CloseableCoroutineScope(SupervisorJob() + ioDispatcher) | ||
| 99 | sessionUrl = url | 114 | sessionUrl = url |
| 100 | sessionToken = token | 115 | sessionToken = token |
| 101 | val joinResponse = client.join(url, token, options) | 116 | val joinResponse = client.join(url, token, options) |
| @@ -104,9 +119,8 @@ internal constructor( | @@ -104,9 +119,8 @@ internal constructor( | ||
| 104 | 119 | ||
| 105 | isSubscriberPrimary = joinResponse.subscriberPrimary | 120 | isSubscriberPrimary = joinResponse.subscriberPrimary |
| 106 | 121 | ||
| 107 | - if (!this::publisher.isInitialized) { | ||
| 108 | - configure(joinResponse, options) | ||
| 109 | - } | 122 | + configure(joinResponse, options) |
| 123 | + | ||
| 110 | // create offer | 124 | // create offer |
| 111 | if (!this.isSubscriberPrimary) { | 125 | if (!this.isSubscriberPrimary) { |
| 112 | negotiate() | 126 | negotiate() |
| @@ -116,7 +130,7 @@ internal constructor( | @@ -116,7 +130,7 @@ internal constructor( | ||
| 116 | } | 130 | } |
| 117 | 131 | ||
| 118 | private fun configure(joinResponse: LivekitRtc.JoinResponse, connectOptions: ConnectOptions?) { | 132 | private fun configure(joinResponse: LivekitRtc.JoinResponse, connectOptions: ConnectOptions?) { |
| 119 | - if (this::publisher.isInitialized || this::subscriber.isInitialized) { | 133 | + if (_publisher != null && _subscriber != null) { |
| 120 | // already configured | 134 | // already configured |
| 121 | return | 135 | return |
| 122 | } | 136 | } |
| @@ -160,13 +174,14 @@ internal constructor( | @@ -160,13 +174,14 @@ internal constructor( | ||
| 160 | enableDtlsSrtp = true | 174 | enableDtlsSrtp = true |
| 161 | } | 175 | } |
| 162 | 176 | ||
| 163 | - | ||
| 164 | - publisher = pctFactory.create( | 177 | + _publisher?.close() |
| 178 | + _publisher = pctFactory.create( | ||
| 165 | rtcConfig, | 179 | rtcConfig, |
| 166 | publisherObserver, | 180 | publisherObserver, |
| 167 | publisherObserver, | 181 | publisherObserver, |
| 168 | ) | 182 | ) |
| 169 | - subscriber = pctFactory.create( | 183 | + _subscriber?.close() |
| 184 | + _subscriber = pctFactory.create( | ||
| 170 | rtcConfig, | 185 | rtcConfig, |
| 171 | subscriberObserver, | 186 | subscriberObserver, |
| 172 | null, | 187 | null, |
| @@ -248,10 +263,15 @@ internal constructor( | @@ -248,10 +263,15 @@ internal constructor( | ||
| 248 | } | 263 | } |
| 249 | 264 | ||
| 250 | fun close() { | 265 | fun close() { |
| 266 | + if (isClosed) { | ||
| 267 | + return | ||
| 268 | + } | ||
| 251 | isClosed = true | 269 | isClosed = true |
| 252 | coroutineScope.close() | 270 | coroutineScope.close() |
| 253 | - publisher.close() | ||
| 254 | - subscriber.close() | 271 | + _publisher?.close() |
| 272 | + _publisher = null | ||
| 273 | + _subscriber?.close() | ||
| 274 | + _subscriber = null | ||
| 255 | client.close() | 275 | client.close() |
| 256 | } | 276 | } |
| 257 | 277 | ||
| @@ -318,8 +338,8 @@ internal constructor( | @@ -318,8 +338,8 @@ internal constructor( | ||
| 318 | } | 338 | } |
| 319 | 339 | ||
| 320 | 340 | ||
| 321 | - listener?.onEngineDisconnected("failed reconnecting.") | ||
| 322 | close() | 341 | close() |
| 342 | + listener?.onEngineDisconnected("failed reconnecting.") | ||
| 323 | } | 343 | } |
| 324 | 344 | ||
| 325 | reconnectingJob = job | 345 | reconnectingJob = job |
| @@ -361,8 +381,8 @@ internal constructor( | @@ -361,8 +381,8 @@ internal constructor( | ||
| 361 | return | 381 | return |
| 362 | } | 382 | } |
| 363 | 383 | ||
| 364 | - if (!this::publisher.isInitialized) { | ||
| 365 | - throw RoomException.ConnectException("Publisher is not connected!") | 384 | + if (_publisher == null) { |
| 385 | + throw RoomException.ConnectException("Publisher isn't setup yet! Is room not connected?!") | ||
| 366 | } | 386 | } |
| 367 | 387 | ||
| 368 | if (!publisher.peerConnection.isConnected() && | 388 | if (!publisher.peerConnection.isConnected() && |
| @@ -572,7 +592,7 @@ internal constructor( | @@ -572,7 +592,7 @@ internal constructor( | ||
| 572 | 592 | ||
| 573 | // Signal error | 593 | // Signal error |
| 574 | override fun onError(error: Throwable) { | 594 | override fun onError(error: Throwable) { |
| 575 | - if (isClosed) { | 595 | + if (connectionState == ConnectionState.CONNECTING) { |
| 576 | listener?.onFailToConnect(error) | 596 | listener?.onFailToConnect(error) |
| 577 | } | 597 | } |
| 578 | } | 598 | } |
| @@ -142,7 +142,7 @@ constructor( | @@ -142,7 +142,7 @@ constructor( | ||
| 142 | } | 142 | } |
| 143 | coroutineScope = CoroutineScope(defaultDispatcher + SupervisorJob()) | 143 | coroutineScope = CoroutineScope(defaultDispatcher + SupervisorJob()) |
| 144 | state = State.CONNECTING | 144 | state = State.CONNECTING |
| 145 | - this.connectOptions = connectOptions | 145 | + connectOptions = options |
| 146 | val response = engine.join(url, token, options) | 146 | val response = engine.join(url, token, options) |
| 147 | LKLog.i { "Connected to server, server version: ${response.serverVersion}, client version: ${Version.CLIENT_VERSION}" } | 147 | LKLog.i { "Connected to server, server version: ${response.serverVersion}, client version: ${Version.CLIENT_VERSION}" } |
| 148 | 148 |
| @@ -9,6 +9,9 @@ import org.mockito.junit.MockitoJUnit | @@ -9,6 +9,9 @@ import org.mockito.junit.MockitoJUnit | ||
| 9 | 9 | ||
| 10 | @ExperimentalCoroutinesApi | 10 | @ExperimentalCoroutinesApi |
| 11 | abstract class BaseTest { | 11 | abstract class BaseTest { |
| 12 | + // Uncomment to enable logging in tests. | ||
| 13 | + //@get:Rule | ||
| 14 | + //var loggingRule = LoggingRule() | ||
| 12 | 15 | ||
| 13 | @get:Rule | 16 | @get:Rule |
| 14 | var mockitoRule = MockitoJUnit.rule() | 17 | var mockitoRule = MockitoJUnit.rule() |
| @@ -2,10 +2,12 @@ package io.livekit.android | @@ -2,10 +2,12 @@ package io.livekit.android | ||
| 2 | 2 | ||
| 3 | import android.content.Context | 3 | import android.content.Context |
| 4 | import androidx.test.core.app.ApplicationProvider | 4 | import androidx.test.core.app.ApplicationProvider |
| 5 | +import io.livekit.android.mock.MockPeerConnection | ||
| 5 | import io.livekit.android.mock.MockWebSocketFactory | 6 | import io.livekit.android.mock.MockWebSocketFactory |
| 6 | import io.livekit.android.mock.dagger.DaggerTestLiveKitComponent | 7 | import io.livekit.android.mock.dagger.DaggerTestLiveKitComponent |
| 7 | import io.livekit.android.mock.dagger.TestCoroutinesModule | 8 | import io.livekit.android.mock.dagger.TestCoroutinesModule |
| 8 | import io.livekit.android.mock.dagger.TestLiveKitComponent | 9 | import io.livekit.android.mock.dagger.TestLiveKitComponent |
| 10 | +import io.livekit.android.room.PeerConnectionTransport | ||
| 9 | import io.livekit.android.room.Room | 11 | import io.livekit.android.room.Room |
| 10 | import io.livekit.android.room.SignalClientTest | 12 | import io.livekit.android.room.SignalClientTest |
| 11 | import io.livekit.android.util.toOkioByteString | 13 | import io.livekit.android.util.toOkioByteString |
| @@ -15,14 +17,16 @@ import okhttp3.Protocol | @@ -15,14 +17,16 @@ import okhttp3.Protocol | ||
| 15 | import okhttp3.Request | 17 | import okhttp3.Request |
| 16 | import okhttp3.Response | 18 | import okhttp3.Response |
| 17 | import org.junit.Before | 19 | import org.junit.Before |
| 20 | +import org.webrtc.PeerConnection | ||
| 18 | 21 | ||
| 19 | @ExperimentalCoroutinesApi | 22 | @ExperimentalCoroutinesApi |
| 20 | abstract class MockE2ETest : BaseTest() { | 23 | abstract class MockE2ETest : BaseTest() { |
| 21 | 24 | ||
| 22 | internal lateinit var component: TestLiveKitComponent | 25 | internal lateinit var component: TestLiveKitComponent |
| 23 | - lateinit var context: Context | ||
| 24 | - lateinit var room: Room | ||
| 25 | - lateinit var wsFactory: MockWebSocketFactory | 26 | + internal lateinit var context: Context |
| 27 | + internal lateinit var room: Room | ||
| 28 | + internal lateinit var wsFactory: MockWebSocketFactory | ||
| 29 | + internal lateinit var subscriber: PeerConnectionTransport | ||
| 26 | 30 | ||
| 27 | @Before | 31 | @Before |
| 28 | fun setup() { | 32 | fun setup() { |
| @@ -37,6 +41,11 @@ abstract class MockE2ETest : BaseTest() { | @@ -37,6 +41,11 @@ abstract class MockE2ETest : BaseTest() { | ||
| 37 | } | 41 | } |
| 38 | 42 | ||
| 39 | suspend fun connect() { | 43 | suspend fun connect() { |
| 44 | + connectSignal() | ||
| 45 | + connectPeerConnection() | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + suspend fun connectSignal() { | ||
| 40 | val job = coroutineRule.scope.launch { | 49 | val job = coroutineRule.scope.launch { |
| 41 | room.connect( | 50 | room.connect( |
| 42 | url = SignalClientTest.EXAMPLE_URL, | 51 | url = SignalClientTest.EXAMPLE_URL, |
| @@ -49,6 +58,13 @@ abstract class MockE2ETest : BaseTest() { | @@ -49,6 +58,13 @@ abstract class MockE2ETest : BaseTest() { | ||
| 49 | job.join() | 58 | job.join() |
| 50 | } | 59 | } |
| 51 | 60 | ||
| 61 | + suspend fun connectPeerConnection() { | ||
| 62 | + subscriber = component.rtcEngine().subscriber | ||
| 63 | + wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.OFFER.toOkioByteString()) | ||
| 64 | + val subPeerConnection = subscriber.peerConnection as MockPeerConnection | ||
| 65 | + subPeerConnection.moveToIceConnectionState(PeerConnection.IceConnectionState.CONNECTED) | ||
| 66 | + } | ||
| 67 | + | ||
| 52 | fun createOpenResponse(request: Request): Response { | 68 | fun createOpenResponse(request: Request): Response { |
| 53 | return Response.Builder() | 69 | return Response.Builder() |
| 54 | .request(request) | 70 | .request(request) |
| @@ -6,7 +6,6 @@ import io.livekit.android.mock.MockWebSocket | @@ -6,7 +6,6 @@ import io.livekit.android.mock.MockWebSocket | ||
| 6 | import io.livekit.android.util.LoggingRule | 6 | import io.livekit.android.util.LoggingRule |
| 7 | import io.livekit.android.util.toPBByteString | 7 | import io.livekit.android.util.toPBByteString |
| 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 9 | -import kotlinx.coroutines.test.runTest | ||
| 10 | import livekit.LivekitRtc | 9 | import livekit.LivekitRtc |
| 11 | import org.junit.Assert | 10 | import org.junit.Assert |
| 12 | import org.junit.Before | 11 | import org.junit.Before |
| @@ -14,18 +13,12 @@ import org.junit.Rule | @@ -14,18 +13,12 @@ import org.junit.Rule | ||
| 14 | import org.junit.Test | 13 | import org.junit.Test |
| 15 | import org.junit.runner.RunWith | 14 | import org.junit.runner.RunWith |
| 16 | import org.robolectric.RobolectricTestRunner | 15 | import org.robolectric.RobolectricTestRunner |
| 17 | -import org.webrtc.PeerConnection | ||
| 18 | -import org.webrtc.SessionDescription | ||
| 19 | 16 | ||
| 20 | 17 | ||
| 21 | @ExperimentalCoroutinesApi | 18 | @ExperimentalCoroutinesApi |
| 22 | @RunWith(RobolectricTestRunner::class) | 19 | @RunWith(RobolectricTestRunner::class) |
| 23 | class RTCEngineMockE2ETest : MockE2ETest() { | 20 | class RTCEngineMockE2ETest : MockE2ETest() { |
| 24 | 21 | ||
| 25 | - | ||
| 26 | - @get:Rule | ||
| 27 | - var loggingRule = LoggingRule() | ||
| 28 | - | ||
| 29 | lateinit var rtcEngine: RTCEngine | 22 | lateinit var rtcEngine: RTCEngine |
| 30 | 23 | ||
| 31 | @Before | 24 | @Before |
| @@ -36,11 +29,10 @@ class RTCEngineMockE2ETest : MockE2ETest() { | @@ -36,11 +29,10 @@ class RTCEngineMockE2ETest : MockE2ETest() { | ||
| 36 | @Test | 29 | @Test |
| 37 | fun iceSubscriberConnect() = runTest { | 30 | fun iceSubscriberConnect() = runTest { |
| 38 | connect() | 31 | connect() |
| 39 | - | ||
| 40 | - val remoteOffer = SessionDescription(SessionDescription.Type.OFFER, "remote_offer") | ||
| 41 | - rtcEngine.onOffer(remoteOffer) | ||
| 42 | - | ||
| 43 | - Assert.assertEquals(remoteOffer, rtcEngine.subscriber.peerConnection.remoteDescription) | 32 | + Assert.assertEquals( |
| 33 | + SignalClientTest.OFFER.offer.sdp, | ||
| 34 | + rtcEngine.subscriber.peerConnection.remoteDescription.description | ||
| 35 | + ) | ||
| 44 | 36 | ||
| 45 | val ws = wsFactory.ws as MockWebSocket | 37 | val ws = wsFactory.ws as MockWebSocket |
| 46 | val sentRequest = LivekitRtc.SignalRequest.newBuilder() | 38 | val sentRequest = LivekitRtc.SignalRequest.newBuilder() |
| @@ -52,9 +44,6 @@ class RTCEngineMockE2ETest : MockE2ETest() { | @@ -52,9 +44,6 @@ class RTCEngineMockE2ETest : MockE2ETest() { | ||
| 52 | Assert.assertTrue(sentRequest.hasAnswer()) | 44 | Assert.assertTrue(sentRequest.hasAnswer()) |
| 53 | Assert.assertEquals(localAnswer.description, sentRequest.answer.sdp) | 45 | Assert.assertEquals(localAnswer.description, sentRequest.answer.sdp) |
| 54 | Assert.assertEquals(localAnswer.type.canonicalForm(), sentRequest.answer.type) | 46 | Assert.assertEquals(localAnswer.type.canonicalForm(), sentRequest.answer.type) |
| 55 | - | ||
| 56 | - subPeerConnection.moveToIceConnectionState(PeerConnection.IceConnectionState.CONNECTED) | ||
| 57 | - | ||
| 58 | Assert.assertEquals(ConnectionState.CONNECTED, rtcEngine.connectionState) | 47 | Assert.assertEquals(ConnectionState.CONNECTED, rtcEngine.connectionState) |
| 59 | } | 48 | } |
| 60 | 49 |
| @@ -3,6 +3,7 @@ package io.livekit.android.room | @@ -3,6 +3,7 @@ package io.livekit.android.room | ||
| 3 | import android.net.Network | 3 | import android.net.Network |
| 4 | import io.livekit.android.MockE2ETest | 4 | import io.livekit.android.MockE2ETest |
| 5 | import io.livekit.android.events.EventCollector | 5 | import io.livekit.android.events.EventCollector |
| 6 | +import io.livekit.android.events.FlowCollector | ||
| 6 | import io.livekit.android.events.RoomEvent | 7 | import io.livekit.android.events.RoomEvent |
| 7 | import io.livekit.android.mock.MockAudioStreamTrack | 8 | import io.livekit.android.mock.MockAudioStreamTrack |
| 8 | import io.livekit.android.mock.MockMediaStream | 9 | import io.livekit.android.mock.MockMediaStream |
| @@ -10,6 +11,8 @@ import io.livekit.android.mock.TestData | @@ -10,6 +11,8 @@ import io.livekit.android.mock.TestData | ||
| 10 | import io.livekit.android.mock.createMediaStreamId | 11 | import io.livekit.android.mock.createMediaStreamId |
| 11 | import io.livekit.android.room.participant.ConnectionQuality | 12 | import io.livekit.android.room.participant.ConnectionQuality |
| 12 | import io.livekit.android.room.track.Track | 13 | import io.livekit.android.room.track.Track |
| 14 | +import io.livekit.android.util.delegate | ||
| 15 | +import io.livekit.android.util.flow | ||
| 13 | import io.livekit.android.util.toOkioByteString | 16 | import io.livekit.android.util.toOkioByteString |
| 14 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 17 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 15 | import kotlinx.coroutines.launch | 18 | import kotlinx.coroutines.launch |
| @@ -25,7 +28,13 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -25,7 +28,13 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 25 | 28 | ||
| 26 | @Test | 29 | @Test |
| 27 | fun connectTest() = runTest { | 30 | fun connectTest() = runTest { |
| 31 | + val collector = FlowCollector(room::state.flow, coroutineRule.scope) | ||
| 28 | connect() | 32 | connect() |
| 33 | + val events = collector.stopCollecting() | ||
| 34 | + Assert.assertEquals(3, events.size) | ||
| 35 | + Assert.assertEquals(Room.State.DISCONNECTED, events[0]) | ||
| 36 | + Assert.assertEquals(Room.State.CONNECTING, events[1]) | ||
| 37 | + Assert.assertEquals(Room.State.CONNECTED, events[2]) | ||
| 29 | } | 38 | } |
| 30 | 39 | ||
| 31 | @Test | 40 | @Test |
| @@ -247,4 +256,11 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -247,4 +256,11 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 247 | Assert.assertEquals(true, events[0] is RoomEvent.Disconnected) | 256 | Assert.assertEquals(true, events[0] is RoomEvent.Disconnected) |
| 248 | } | 257 | } |
| 249 | 258 | ||
| 259 | + @Test | ||
| 260 | + fun reconnectAfterDisconnect() = runTest { | ||
| 261 | + connect() | ||
| 262 | + room.disconnect() | ||
| 263 | + connect() | ||
| 264 | + Assert.assertEquals(room.state, Room.State.CONNECTED) | ||
| 265 | + } | ||
| 250 | } | 266 | } |
| @@ -174,7 +174,7 @@ class SignalClientTest : BaseTest() { | @@ -174,7 +174,7 @@ class SignalClientTest : BaseTest() { | ||
| 174 | 174 | ||
| 175 | val OFFER = with(LivekitRtc.SignalResponse.newBuilder()) { | 175 | val OFFER = with(LivekitRtc.SignalResponse.newBuilder()) { |
| 176 | offer = with(offerBuilder) { | 176 | offer = with(offerBuilder) { |
| 177 | - sdp = "" | 177 | + sdp = "remote_offer" |
| 178 | type = "offer" | 178 | type = "offer" |
| 179 | build() | 179 | build() |
| 180 | } | 180 | } |
| @@ -7,9 +7,12 @@ import org.junit.runner.Description | @@ -7,9 +7,12 @@ import org.junit.runner.Description | ||
| 7 | import org.junit.runners.model.Statement | 7 | import org.junit.runners.model.Statement |
| 8 | import timber.log.Timber | 8 | import timber.log.Timber |
| 9 | 9 | ||
| 10 | +/** | ||
| 11 | + * Add this rule to a test class to turn on logs. | ||
| 12 | + */ | ||
| 10 | class LoggingRule : TestRule { | 13 | class LoggingRule : TestRule { |
| 11 | 14 | ||
| 12 | - val logTree = object : Timber.Tree() { | 15 | + val logTree = object : Timber.DebugTree() { |
| 13 | override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { | 16 | override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { |
| 14 | val priorityChar = when (priority) { | 17 | val priorityChar = when (priority) { |
| 15 | Log.VERBOSE -> "v" | 18 | Log.VERBOSE -> "v" |
| @@ -32,8 +35,8 @@ class LoggingRule : TestRule { | @@ -32,8 +35,8 @@ class LoggingRule : TestRule { | ||
| 32 | override fun apply(base: Statement, description: Description?) = object : Statement() { | 35 | override fun apply(base: Statement, description: Description?) = object : Statement() { |
| 33 | override fun evaluate() { | 36 | override fun evaluate() { |
| 34 | val oldLoggingLevel = LiveKit.loggingLevel | 37 | val oldLoggingLevel = LiveKit.loggingLevel |
| 35 | - LiveKit.loggingLevel = LoggingLevel.VERBOSE | ||
| 36 | Timber.plant(logTree) | 38 | Timber.plant(logTree) |
| 39 | + LiveKit.loggingLevel = LoggingLevel.VERBOSE | ||
| 37 | base.evaluate() | 40 | base.evaluate() |
| 38 | Timber.uproot(logTree) | 41 | Timber.uproot(logTree) |
| 39 | LiveKit.loggingLevel = oldLoggingLevel | 42 | LiveKit.loggingLevel = oldLoggingLevel |
| @@ -248,13 +248,24 @@ class CallViewModel( | @@ -248,13 +248,24 @@ class CallViewModel( | ||
| 248 | room.value?.localParticipant?.setTrackSubscriptionPermissions(mutablePermissionAllowed.value) | 248 | room.value?.localParticipant?.setTrackSubscriptionPermissions(mutablePermissionAllowed.value) |
| 249 | } | 249 | } |
| 250 | 250 | ||
| 251 | - fun simulateMigration(){ | 251 | + fun simulateMigration() { |
| 252 | room.value?.sendSimulateScenario( | 252 | room.value?.sendSimulateScenario( |
| 253 | LivekitRtc.SimulateScenario.newBuilder() | 253 | LivekitRtc.SimulateScenario.newBuilder() |
| 254 | .setMigration(true) | 254 | .setMigration(true) |
| 255 | .build() | 255 | .build() |
| 256 | ) | 256 | ) |
| 257 | } | 257 | } |
| 258 | + | ||
| 259 | + fun reconnect() { | ||
| 260 | + room.value?.disconnect() | ||
| 261 | + | ||
| 262 | + viewModelScope.launch { | ||
| 263 | + room.value?.connect( | ||
| 264 | + url, | ||
| 265 | + token | ||
| 266 | + ) | ||
| 267 | + } | ||
| 268 | + } | ||
| 258 | } | 269 | } |
| 259 | 270 | ||
| 260 | private fun <T> LiveData<T>.hide(): LiveData<T> = this | 271 | private fun <T> LiveData<T>.hide(): LiveData<T> = this |
| @@ -111,6 +111,7 @@ class CallActivity : AppCompatActivity() { | @@ -111,6 +111,7 @@ class CallActivity : AppCompatActivity() { | ||
| 111 | onExitClick = { finish() }, | 111 | onExitClick = { finish() }, |
| 112 | onSendMessage = { viewModel.sendData(it) }, | 112 | onSendMessage = { viewModel.sendData(it) }, |
| 113 | onSimulateMigration = { viewModel.simulateMigration() }, | 113 | onSimulateMigration = { viewModel.simulateMigration() }, |
| 114 | + fullReconnect = { viewModel.reconnect() }, | ||
| 114 | ) | 115 | ) |
| 115 | } | 116 | } |
| 116 | } | 117 | } |
| @@ -159,6 +160,7 @@ class CallActivity : AppCompatActivity() { | @@ -159,6 +160,7 @@ class CallActivity : AppCompatActivity() { | ||
| 159 | onSnackbarDismiss: () -> Unit = {}, | 160 | onSnackbarDismiss: () -> Unit = {}, |
| 160 | onSendMessage: (String) -> Unit = {}, | 161 | onSendMessage: (String) -> Unit = {}, |
| 161 | onSimulateMigration: () -> Unit = {}, | 162 | onSimulateMigration: () -> Unit = {}, |
| 163 | + fullReconnect: () -> Unit = {}, | ||
| 162 | ) { | 164 | ) { |
| 163 | AppTheme(darkTheme = true) { | 165 | AppTheme(darkTheme = true) { |
| 164 | ConstraintLayout( | 166 | ConstraintLayout( |
| @@ -410,7 +412,8 @@ class CallActivity : AppCompatActivity() { | @@ -410,7 +412,8 @@ class CallActivity : AppCompatActivity() { | ||
| 410 | if (showDebugDialog) { | 412 | if (showDebugDialog) { |
| 411 | DebugMenuDialog( | 413 | DebugMenuDialog( |
| 412 | onDismissRequest = { showDebugDialog = false }, | 414 | onDismissRequest = { showDebugDialog = false }, |
| 413 | - simulateMigration = { onSimulateMigration() } | 415 | + simulateMigration = { onSimulateMigration() }, |
| 416 | + fullReconnect = { fullReconnect() }, | ||
| 414 | ) | 417 | ) |
| 415 | } | 418 | } |
| 416 | } | 419 | } |
| @@ -17,7 +17,8 @@ import androidx.compose.ui.window.Dialog | @@ -17,7 +17,8 @@ import androidx.compose.ui.window.Dialog | ||
| 17 | @Composable | 17 | @Composable |
| 18 | fun DebugMenuDialog( | 18 | fun DebugMenuDialog( |
| 19 | onDismissRequest: () -> Unit = {}, | 19 | onDismissRequest: () -> Unit = {}, |
| 20 | - simulateMigration: () -> Unit = {} | 20 | + simulateMigration: () -> Unit = {}, |
| 21 | + fullReconnect: () -> Unit = {}, | ||
| 21 | ) { | 22 | ) { |
| 22 | Dialog(onDismissRequest = onDismissRequest) { | 23 | Dialog(onDismissRequest = onDismissRequest) { |
| 23 | Column( | 24 | Column( |
| @@ -36,6 +37,11 @@ fun DebugMenuDialog( | @@ -36,6 +37,11 @@ fun DebugMenuDialog( | ||
| 36 | }) { | 37 | }) { |
| 37 | Text("Simulate Migration") | 38 | Text("Simulate Migration") |
| 38 | } | 39 | } |
| 40 | + Button(onClick = { | ||
| 41 | + fullReconnect() | ||
| 42 | + }) { | ||
| 43 | + Text("Reconnect to room") | ||
| 44 | + } | ||
| 39 | } | 45 | } |
| 40 | } | 46 | } |
| 41 | } | 47 | } |
-
请 注册 或 登录 后发表评论