diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt index fdf5ba1fad..37e2bb4291 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt @@ -64,7 +64,9 @@ internal class CoordinatorConnectionModule( } // API - override val http: OkHttpClient = OkHttpClient.Builder().addInterceptor(HeadersInterceptor()) + override val http: OkHttpClient = OkHttpClient.Builder().addInterceptor( + HeadersInterceptor(context), + ) .addInterceptor(authInterceptor).addInterceptor( HttpLoggingInterceptor().apply { level = loggingLevel.httpLoggingLevel.level diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt index 63c248e36d..8928dd7b24 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt @@ -16,16 +16,71 @@ package io.getstream.video.android.core.internal.module +import android.content.Context +import android.os.Build import io.getstream.video.android.core.StreamVideo import okhttp3.Interceptor import okhttp3.Response +import java.text.Normalizer + +internal class HeadersInterceptor(context: Context) : Interceptor { + + private val userAgent = buildUserAgent(context) -internal class HeadersInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() .newBuilder() + .addHeader("User-Agent", userAgent) .addHeader("X-Stream-Client", StreamVideo.buildSdkTrackingHeaders()) .build() return chain.proceed(request) } + + private fun buildUserAgent(context: Context): String { + with(context.packageManager) { + val versionName = runCatching { + getPackageInfo(context.packageName, 0).versionName + }.getOrNull() ?: "nameNotFound" + val versionCode = runCatching { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + getPackageInfo(context.packageName, 0).longVersionCode.toString() + } else { + getPackageInfo(context.packageName, 0).versionCode.toString() + } + }.getOrNull() ?: "versionCodeNotFound" + + val applicationInfo = context.applicationInfo + val stringId = applicationInfo.labelRes + val appName = + if (stringId == 0) { + applicationInfo.nonLocalizedLabel.toString() + } else { + context.getString(stringId) + } + + val manufacturer = Build.MANUFACTURER + val model = Build.MODEL + val version = Build.VERSION.SDK_INT + val versionRelease = Build.VERSION.RELEASE + + val installerName = runCatching { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + getInstallSourceInfo(context.packageName).installingPackageName + } else { + getInstallerPackageName(context.packageName) + } + }.getOrNull() ?: "StandAloneInstall" + + return ( + "$appName / $versionName($versionCode); $installerName; ($manufacturer; " + + "$model; SDK $version; Android $versionRelease)" + ) + .sanitize() + } + } + + private fun String.sanitize(): String { + return Normalizer.normalize(this, Normalizer.Form.NFD) + .replace("[^\\p{ASCII}]".toRegex(), "") + } }