diff --git a/app/src/main/java/app/waveflow/WaveFlowApp.kt b/app/src/main/java/app/waveflow/WaveFlowApp.kt index 558e346..159bc8c 100644 --- a/app/src/main/java/app/waveflow/WaveFlowApp.kt +++ b/app/src/main/java/app/waveflow/WaveFlowApp.kt @@ -24,7 +24,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch -import okhttp3.OkHttpClient /** * Point d'entrée de l'application. @@ -52,9 +51,7 @@ class WaveFlowApp : Application(), ImageLoaderFactory { */ override fun newImageLoader(): ImageLoader = ImageLoader.Builder(this) .okHttpClient { - OkHttpClient.Builder() - .addInterceptor(ServerImageAuthInterceptor(container.serverSessionRepository)) - .build() + ServerHttp.imageClient(ServerImageAuthInterceptor(container.serverSessionRepository)) } .build() } diff --git a/app/src/main/java/app/waveflow/data/remote/ServerHttp.kt b/app/src/main/java/app/waveflow/data/remote/ServerHttp.kt index 64568cf..b792dd9 100644 --- a/app/src/main/java/app/waveflow/data/remote/ServerHttp.kt +++ b/app/src/main/java/app/waveflow/data/remote/ServerHttp.kt @@ -11,6 +11,7 @@ import okhttp3.Call import okhttp3.Callback import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request @@ -199,6 +200,19 @@ class ServerHttp( fun defaultClient(): OkHttpClient = OkHttpClient.Builder() .callTimeout(CALL_TIMEOUT) .build() + + /** + * Le client des pochettes, muni du même plafond que les appels d'API. + * + * Coil construit son propre client : sans passer par ici, il repartirait + * sur les délais par étape d'OkHttp, dont aucun ne borne l'appel entier. + * Une pochette qui n'arrive jamais retiendrait alors son fil de bout en + * bout. Le plafond se décide à un seul endroit, celui-ci. + */ + fun imageClient(auth: Interceptor): OkHttpClient = OkHttpClient.Builder() + .callTimeout(CALL_TIMEOUT) + .addInterceptor(auth) + .build() } } diff --git a/app/src/main/java/app/waveflow/ui/server/ServerScreen.kt b/app/src/main/java/app/waveflow/ui/server/ServerScreen.kt index 1dd7134..f4d461d 100644 --- a/app/src/main/java/app/waveflow/ui/server/ServerScreen.kt +++ b/app/src/main/java/app/waveflow/ui/server/ServerScreen.kt @@ -279,8 +279,8 @@ private fun ConnectedAccount( Spacer(Modifier.height(32.dp)) Text( - text = "Le catalogue de ce serveur est consultable depuis l'onglet Serveur. " + - "La lecture à distance arrive dans une prochaine version.", + text = "Le catalogue de ce serveur est consultable depuis l'onglet Serveur, " + + "et ses pistes se lisent comme celles de l'appareil.", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, ) diff --git a/app/src/test/java/app/waveflow/data/remote/ServerImageAuthInterceptorTest.kt b/app/src/test/java/app/waveflow/data/remote/ServerImageAuthInterceptorTest.kt index c98ede4..f45d373 100644 --- a/app/src/test/java/app/waveflow/data/remote/ServerImageAuthInterceptorTest.kt +++ b/app/src/test/java/app/waveflow/data/remote/ServerImageAuthInterceptorTest.kt @@ -5,6 +5,7 @@ import app.waveflow.testing.FakeServerApi import app.waveflow.testing.FakeSessionStore import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.mockwebserver.Dispatcher @@ -14,6 +15,7 @@ import okhttp3.mockwebserver.RecordedRequest import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -77,6 +79,18 @@ class ServerImageAuthInterceptorTest { client.newCall(Request.Builder().url(target).build()).execute().close() } + @Test + fun `le client des pochettes borne l'appel entier`() { + // Les délais par défaut d'OkHttp portent sur chaque étape prise à part ; + // aucun ne borne l'appel. Une pochette qui n'arrive jamais retiendrait + // alors son fil de bout en bout. Le plafond est celui des appels d'API : + // les comparer garantit qu'un seul endroit continue d'en décider. + val client = ServerHttp.imageClient(Interceptor { it.proceed(it.request()) }) + + assertTrue("le plafond doit être posé", client.callTimeoutMillis > 0) + assertEquals(ServerHttp.defaultClient().callTimeoutMillis, client.callTimeoutMillis) + } + @Test fun `une pochette du serveur connecte porte le jeton`() = runTest { server.enqueue(MockResponse().setBody("image"))