Skip to content

Commit 8619650

Browse files
Improve Coordinator Socket reconnection with TokenRepository (#1580)
* feat: add video moderation * feat: add video moderation * feat: add video moderation * feat: add video moderation * Make ModerationWarningUiContainer as public api * remove dependency of stream-video-android-filters-video from android-ui-compose * remove all public-apis * revert BlurIntensity enum * revert BlurIntensity enum * revert changes in strings.xml * sdk will render moderation warning ui and will blur the video * sdk will render moderation warning ui and will blur the video * sdk will render moderation warning ui and will blur the video * Add default moderation filter * Spotless * update * Spotless * refactor all moderation related configuration into ModerationConfig * refactor all moderation related configuration into ModerationConfig * refactor all moderation related configuration into ModerationConfig * refactor * add ut and comments * add ut and comments * minor changes * Make blurring a part of core-sdk * rename methods * add token repository * add expiry-time in demo-app * spotless and apidump * single thread coordinator socket * single thread coordinator socket * single thread coordinator socket * single thread coordinator socket * refactor api files * refactor * 1. increase token expiry time 2. Refactor usage token * 1. increase token expiry time 2. Refactor usage token * 1. Update token provider * chore: remove comments * chore: improve token provider * chore: update FakeTokenManager with PersistingTokenProvider * chore: Improve socket disconnection logging and add TODO The log message for socket disconnection now includes the specific error for better debugging. Additionally, a TODO comment was added to the `sendData` method in `SocketActions.kt` to refactor it to return a boolean in the future. * remove comments * chore: remove logs and commented out code * exclude StreamVideoBuilderTest test from standard test * exclude StreamVideoBuilderTest test from standard test * add unit tests --------- Co-authored-by: Aleksandar Apostolov <[email protected]>
1 parent cb464d3 commit 8619650

28 files changed

+502
-55
lines changed

demo-app/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,9 @@ dependencies {
292292

293293
implementation(libs.audioswitch)
294294

295+
// Logging
296+
implementation(libs.okhttp.logging)
297+
295298
// Also Leak Canary added in the previous block
296299

297300
// Instrumentation tests

demo-app/src/main/kotlin/io/getstream/video/android/data/services/stream/StreamService.kt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717
package io.getstream.video.android.data.services.stream
1818

1919
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
20+
import io.getstream.log.streamLog
2021
import io.getstream.video.android.model.User
2122
import io.getstream.video.android.models.UserCredentials
2223
import io.getstream.video.android.models.builtInCredentials
2324
import kotlinx.serialization.json.Json
2425
import okhttp3.MediaType.Companion.toMediaType
26+
import okhttp3.OkHttpClient
27+
import okhttp3.logging.HttpLoggingInterceptor
2528
import retrofit2.Retrofit
2629
import retrofit2.create
2730
import retrofit2.http.GET
@@ -32,23 +35,34 @@ fun interface StreamService {
3235
suspend fun getAuthData(
3336
@Query("environment") environment: String,
3437
@Query("user_id") userId: String?,
38+
@Query("exp") exp: Int,
3539
): GetAuthDataResponse
3640

3741
companion object {
3842
private const val BASE_URL = "https://pronto.getstream.io/"
43+
const val TOKEN_EXPIRY_TIME = 7 * 24 * 60 * 60 // 7d * 24 hrs * 60 mins * 60 sec
3944

4045
private val json = Json { ignoreUnknownKeys = true }
41-
46+
private val okHttpClient = OkHttpClient.Builder()
47+
.addInterceptor(
48+
HttpLoggingInterceptor {
49+
streamLog(tag = "Video:Http") { it }
50+
}.apply {
51+
level = HttpLoggingInterceptor.Level.BODY
52+
},
53+
)
54+
.build()
4255
private val retrofit = Retrofit.Builder()
4356
.baseUrl(BASE_URL)
4457
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
58+
.client(okHttpClient)
4559
.build()
4660

4761
private val serviceInstance = retrofit.create<StreamService>()
4862

49-
val instance = StreamService { environment, userId ->
63+
val instance = StreamService { environment, userId, exp ->
5064
User.builtInCredentials[userId]?.toAuthDataResponse()
51-
?: serviceInstance.getAuthData(environment, userId)
65+
?: serviceInstance.getAuthData(environment, userId, exp)
5266
}
5367
}
5468
}

demo-app/src/main/kotlin/io/getstream/video/android/ui/login/LoginViewModel.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class LoginViewModel @Inject constructor(
9292
val authData = StreamService.instance.getAuthData(
9393
environment = it.env,
9494
userId = userId,
95+
StreamService.TOKEN_EXPIRY_TIME,
9596
)
9697
val loggedInGoogleUser =
9798
if (autoLogIn) null else googleAccountRepository.getCurrentUser()
@@ -128,6 +129,7 @@ class LoginViewModel @Inject constructor(
128129
val authData = StreamService.instance.getAuthData(
129130
environment = it.env,
130131
userId = user.id,
132+
StreamService.TOKEN_EXPIRY_TIME,
131133
)
132134
// Store the data in the demo app
133135
dataStore.updateUser(user)

demo-app/src/main/kotlin/io/getstream/video/android/util/StreamVideoInitHelper.kt

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import io.getstream.video.android.util.config.AppConfig
6161
import kotlinx.coroutines.flow.MutableStateFlow
6262
import kotlinx.coroutines.flow.StateFlow
6363
import kotlinx.coroutines.flow.firstOrNull
64+
import kotlinx.coroutines.runBlocking
6465

6566
public enum class InitializedState {
6667
NOT_STARTED, RUNNING, FINISHED, FAILED
@@ -124,6 +125,7 @@ object StreamVideoInitHelper {
124125
authData = StreamService.instance.getAuthData(
125126
environment = AppConfig.currentEnvironment.value!!.env,
126127
userId = userId,
128+
StreamService.TOKEN_EXPIRY_TIME,
127129
)
128130

129131
loggedInUser = User(id = authData.userId, role = "admin")
@@ -140,6 +142,7 @@ object StreamVideoInitHelper {
140142
authData = StreamService.instance.getAuthData(
141143
environment = AppConfig.currentEnvironment.value!!.env,
142144
userId = loggedInUser.id,
145+
StreamService.TOKEN_EXPIRY_TIME,
143146
)
144147
}
145148

@@ -197,7 +200,19 @@ object StreamVideoInitHelper {
197200

198201
chatClient.connectUser(
199202
user = chatUser,
200-
token = token,
203+
tokenProvider = object : io.getstream.chat.android.client.token.TokenProvider {
204+
override fun loadToken(): String {
205+
return runBlocking {
206+
val email = user.custom?.get("email")
207+
val authData = StreamService.instance.getAuthData(
208+
environment = AppConfig.currentEnvironment.value!!.env,
209+
userId = email,
210+
StreamService.TOKEN_EXPIRY_TIME,
211+
)
212+
authData.token
213+
}
214+
}
215+
},
201216
).enqueue()
202217
}
203218

@@ -303,10 +318,13 @@ object StreamVideoInitHelper {
303318
),
304319
tokenProvider = object : TokenProvider {
305320
override suspend fun loadToken(): String {
306-
val email = user.custom?.get("email")
321+
val userEmail = user.custom?.get("email")
322+
val userId = user.id
323+
val userIdForTokenRenewal = if (userEmail.isNullOrEmpty()) userId else userEmail
307324
val authData = StreamService.instance.getAuthData(
308325
environment = AppConfig.currentEnvironment.value!!.env,
309-
userId = email,
326+
userId = userIdForTokenRenewal,
327+
StreamService.TOKEN_EXPIRY_TIME,
310328
)
311329
return authData.token
312330
}

stream-video-android-core/api/stream-video-android-core.api

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13118,9 +13118,17 @@ public abstract interface class io/getstream/video/android/core/socket/common/to
1311813118
public abstract fun loadToken (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1311913119
}
1312013120

13121+
public final class io/getstream/video/android/core/socket/common/token/TokenRepository {
13122+
public fun <init> ()V
13123+
public fun <init> (Ljava/lang/String;)V
13124+
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
13125+
public final fun getToken ()Ljava/lang/String;
13126+
public final fun updateToken (Ljava/lang/String;)V
13127+
}
13128+
1312113129
public class io/getstream/video/android/core/socket/coordinator/CoordinatorSocketConnection : io/getstream/video/android/core/socket/common/SocketListener, io/getstream/video/android/core/socket/common/SocketActions {
13122-
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lio/getstream/video/android/model/User;Ljava/lang/String;Lokhttp3/OkHttpClient;Lio/getstream/video/android/core/internal/network/NetworkStateProvider;Lkotlinx/coroutines/CoroutineScope;Landroidx/lifecycle/Lifecycle;Lio/getstream/video/android/core/socket/common/token/TokenProvider;)V
13123-
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lio/getstream/video/android/model/User;Ljava/lang/String;Lokhttp3/OkHttpClient;Lio/getstream/video/android/core/internal/network/NetworkStateProvider;Lkotlinx/coroutines/CoroutineScope;Landroidx/lifecycle/Lifecycle;Lio/getstream/video/android/core/socket/common/token/TokenProvider;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
13130+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lio/getstream/video/android/model/User;Ljava/lang/String;Lokhttp3/OkHttpClient;Lio/getstream/video/android/core/internal/network/NetworkStateProvider;Lkotlinx/coroutines/CoroutineScope;Landroidx/lifecycle/Lifecycle;Lio/getstream/video/android/core/socket/common/token/TokenProvider;Lio/getstream/video/android/core/socket/common/token/TokenRepository;)V
13131+
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lio/getstream/video/android/model/User;Ljava/lang/String;Lokhttp3/OkHttpClient;Lio/getstream/video/android/core/internal/network/NetworkStateProvider;Lkotlinx/coroutines/CoroutineScope;Landroidx/lifecycle/Lifecycle;Lio/getstream/video/android/core/socket/common/token/TokenProvider;Lio/getstream/video/android/core/socket/common/token/TokenRepository;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
1312413132
public fun connect (Lio/getstream/video/android/model/User;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1312513133
public synthetic fun connect (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1312613134
public fun connectionId ()Lkotlinx/coroutines/flow/StateFlow;
@@ -13327,8 +13335,8 @@ public final class io/getstream/video/android/core/socket/coordinator/state/Vide
1332713335

1332813336
public final class io/getstream/video/android/core/socket/sfu/SfuSocketConnection : io/getstream/video/android/core/socket/common/SocketListener, io/getstream/video/android/core/socket/common/SocketActions {
1332913337
public static final field Companion Lio/getstream/video/android/core/socket/sfu/SfuSocketConnection$Companion;
13330-
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lokhttp3/OkHttpClient;Lio/getstream/video/android/core/internal/network/NetworkStateProvider;Lkotlinx/coroutines/CoroutineScope;Landroidx/lifecycle/Lifecycle;Lio/getstream/video/android/core/socket/common/token/TokenProvider;)V
13331-
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lokhttp3/OkHttpClient;Lio/getstream/video/android/core/internal/network/NetworkStateProvider;Lkotlinx/coroutines/CoroutineScope;Landroidx/lifecycle/Lifecycle;Lio/getstream/video/android/core/socket/common/token/TokenProvider;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
13338+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lokhttp3/OkHttpClient;Lio/getstream/video/android/core/internal/network/NetworkStateProvider;Lkotlinx/coroutines/CoroutineScope;Landroidx/lifecycle/Lifecycle;Lio/getstream/video/android/core/socket/common/token/TokenProvider;Lio/getstream/video/android/core/socket/common/token/TokenRepository;)V
13339+
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lokhttp3/OkHttpClient;Lio/getstream/video/android/core/internal/network/NetworkStateProvider;Lkotlinx/coroutines/CoroutineScope;Landroidx/lifecycle/Lifecycle;Lio/getstream/video/android/core/socket/common/token/TokenProvider;Lio/getstream/video/android/core/socket/common/token/TokenRepository;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
1333213340
public synthetic fun connect (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1333313341
public fun connect (Lstream/video/sfu/event/JoinRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1333413342
public fun connectionId ()Lkotlinx/coroutines/flow/StateFlow;

stream-video-android-core/build.gradle.kts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,27 @@ mavenPublishing {
240240
),
241241
)
242242
}
243+
244+
afterEvaluate {
245+
tasks.named("testDebugUnitTest") {
246+
dependsOn("isolatedTest")
247+
}
248+
}
249+
250+
tasks.register<Test>("isolatedTest") {
251+
252+
description = "Runs StreamVideoBuilderTest in an isolation"
253+
group = "verification"
254+
255+
// Only this class
256+
include("**/StreamVideoBuilderTest.class")
257+
258+
// Force new JVM
259+
forkEvery = 1
260+
261+
// Reuse Android's debug unit test configuration
262+
val androidTestTask = tasks.named<Test>("testDebugUnitTest").get()
263+
264+
testClassesDirs = androidTestTask.testClassesDirs
265+
classpath = androidTestTask.classpath
266+
}

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoBuilder.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ import io.getstream.video.android.core.permission.android.DefaultStreamPermissio
3737
import io.getstream.video.android.core.permission.android.StreamPermissionCheck
3838
import io.getstream.video.android.core.socket.common.scope.ClientScope
3939
import io.getstream.video.android.core.socket.common.scope.UserScope
40-
import io.getstream.video.android.core.socket.common.token.ConstantTokenProvider
40+
import io.getstream.video.android.core.socket.common.token.RepositoryTokenProvider
4141
import io.getstream.video.android.core.socket.common.token.TokenProvider
42+
import io.getstream.video.android.core.socket.common.token.TokenRepository
4243
import io.getstream.video.android.core.sounds.RingingCallVibrationConfig
4344
import io.getstream.video.android.core.sounds.Sounds
4445
import io.getstream.video.android.core.sounds.defaultResourcesRingingConfig
@@ -95,6 +96,7 @@ import java.net.ConnectException
9596
* @see ClientState.connection
9697
*
9798
*/
99+
98100
public class StreamVideoBuilder @JvmOverloads constructor(
99101
context: Context,
100102
private val apiKey: ApiKey,
@@ -106,7 +108,7 @@ public class StreamVideoBuilder @JvmOverloads constructor(
106108
object : TokenProvider {
107109
override suspend fun loadToken(): String = legacy.invoke(null)
108110
}
109-
} ?: ConstantTokenProvider(token),
111+
} ?: RepositoryTokenProvider(tokenRepository),
110112
private val loggingLevel: LoggingLevel = LoggingLevel(),
111113
private val notificationConfig: NotificationConfig = NotificationConfig(),
112114
private val ringNotification: ((call: Call) -> Notification?)? = null,
@@ -214,7 +216,7 @@ public class StreamVideoBuilder @JvmOverloads constructor(
214216

215217
// Android JSR-310 backport backport
216218
AndroidThreeTen.init(context)
217-
219+
tokenRepository.updateToken(token)
218220
// This connection module class exposes the connections to the various retrofit APIs.
219221
val coordinatorConnectionModule = CoordinatorConnectionModule(
220222
context = context,
@@ -225,9 +227,9 @@ public class StreamVideoBuilder @JvmOverloads constructor(
225227
loggingLevel = loggingLevel,
226228
user = user,
227229
apiKey = apiKey,
228-
userToken = token,
229230
tokenProvider = tokenProvider,
230231
lifecycle = lifecycle,
232+
tokenRepository = tokenRepository,
231233
)
232234

233235
val deviceTokenStorage = DeviceTokenStorage(context)
@@ -272,6 +274,7 @@ public class StreamVideoBuilder @JvmOverloads constructor(
272274
vibrationConfig = vibrationConfig,
273275
enableStereoForSubscriber = enableStereoForSubscriber,
274276
telecomConfig = telecomConfig,
277+
tokenRepository = tokenRepository,
275278
)
276279

277280
if (user.type == UserType.Guest) {
@@ -348,6 +351,11 @@ public class StreamVideoBuilder @JvmOverloads constructor(
348351
}
349352
}
350353

354+
/**
355+
* Refactor Later
356+
*/
357+
internal val tokenRepository = TokenRepository("")
358+
351359
sealed class GEO {
352360
/** Run calls over our global edge network, this is the default and right for most applications */
353361
object GlobalEdgeNetwork : GEO()

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,9 @@ import io.getstream.video.android.core.permission.android.DefaultStreamPermissio
107107
import io.getstream.video.android.core.permission.android.StreamPermissionCheck
108108
import io.getstream.video.android.core.socket.ErrorResponse
109109
import io.getstream.video.android.core.socket.common.scope.ClientScope
110-
import io.getstream.video.android.core.socket.common.token.ConstantTokenProvider
110+
import io.getstream.video.android.core.socket.common.token.RepositoryTokenProvider
111111
import io.getstream.video.android.core.socket.common.token.TokenProvider
112+
import io.getstream.video.android.core.socket.common.token.TokenRepository
112113
import io.getstream.video.android.core.socket.coordinator.state.VideoSocketState
113114
import io.getstream.video.android.core.sounds.CallSoundAndVibrationPlayer
114115
import io.getstream.video.android.core.sounds.RingingCallVibrationConfig
@@ -161,7 +162,8 @@ internal class StreamVideoClient internal constructor(
161162
internal var token: String,
162163
private val lifecycle: Lifecycle,
163164
internal val coordinatorConnectionModule: CoordinatorConnectionModule,
164-
internal val tokenProvider: TokenProvider = ConstantTokenProvider(token),
165+
internal val tokenRepository: TokenRepository,
166+
internal val tokenProvider: TokenProvider = RepositoryTokenProvider(tokenRepository),
165167
internal val streamNotificationManager: StreamNotificationManager,
166168
internal val enableCallNotificationUpdates: Boolean,
167169
internal val callServiceConfigRegistry: CallServiceConfigRegistry = CallServiceConfigRegistry(),
@@ -273,6 +275,7 @@ internal class StreamVideoClient internal constructor(
273275
// Retry once with a new token if the token is expired
274276
if (e.isAuthError()) {
275277
val newToken = tokenProvider.loadToken()
278+
tokenRepository.updateToken(newToken)
276279
token = newToken
277280
coordinatorConnectionModule.updateToken(newToken)
278281
apiCall()

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/call/RtcSession.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import io.getstream.video.android.core.model.VideoTrack
7878
import io.getstream.video.android.core.model.toPeerType
7979
import io.getstream.video.android.core.socket.common.VideoParser
8080
import io.getstream.video.android.core.socket.common.parser2.MoshiVideoParser
81+
import io.getstream.video.android.core.socket.common.token.TokenRepository
8182
import io.getstream.video.android.core.socket.sfu.state.SfuSocketState
8283
import io.getstream.video.android.core.toJson
8384
import io.getstream.video.android.core.trace.PeerConnectionTraceKey
@@ -236,12 +237,12 @@ public class RtcSession internal constructor(
236237
apiUrl = sfuUrl,
237238
wssUrl = sfuWsUrl,
238239
connectionTimeoutInMs = 2000L,
239-
userToken = sfuToken,
240240
lifecycle = lifecycle,
241241
onSignalingLost = { error ->
242242
call.debug.fastReconnect()
243243
},
244244
tracer = sfuTracer,
245+
tokenRepository = TokenRepository(sfuToken),
245246
)
246247
},
247248
) {

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/ConnectionModuleDeclaration.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,6 @@ internal interface ConnectionModuleDeclaration<Api, SocketConnection, Http : OkH
7676
*/
7777
val loggingLevel: LoggingLevel get() = LoggingLevel()
7878

79-
/**
80-
* The user token.
81-
*/
82-
val userToken: Token
83-
8479
/**
8580
* The lifecycle of the application.
8681
*/

0 commit comments

Comments
 (0)