Add :libraries:network module

This commit is contained in:
Benoit Marty 2023-03-16 15:31:27 +01:00 committed by Benoit Marty
parent 32d9a183ae
commit a68b3f80eb
11 changed files with 239 additions and 11 deletions

View file

@ -219,6 +219,9 @@ dependencies {
implementation(libs.androidx.startup)
implementation(libs.coil)
implementation(platform(libs.network.okhttp.bom))
implementation("com.squareup.okhttp3:logging-interceptor")
implementation(libs.dagger)
kapt(libs.dagger.compiler)

View file

@ -33,6 +33,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.plus
import okhttp3.logging.HttpLoggingInterceptor
import java.io.File
import java.util.concurrent.Executors
@ -64,6 +65,7 @@ object AppModule {
gitBranchName = "TODO", // BuildConfig.GIT_BRANCH_NAME,
flavorDescription = "TODO", // BuildConfig.FLAVOR_DESCRIPTION,
flavorShortDescription = "TODO", // BuildConfig.SHORT_FLAVOR_DESCRIPTION,
okHttpLoggingLevel = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.BASIC,
)
@Provides

View file

@ -92,6 +92,11 @@ accompanist_flowlayout = { module = "com.google.accompanist:accompanist-flowlayo
# Libraries
squareup_seismic = "com.squareup:seismic:1.0.3"
# network
network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:4.10.0"
network_retrofit = "com.squareup.retrofit2:retrofit:2.9.0"
network_retrofit_converter_serialization = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
# Test
test_core = { module = "androidx.test:core", version.ref = "test_core" }
test_corektx = { module = "androidx.test:core-ktx", version.ref = "test_core" }

View file

@ -1,4 +1,3 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
@ -29,4 +28,6 @@ java {
dependencies {
implementation(libs.coroutines.core)
implementation(platform(libs.network.okhttp.bom))
implementation("com.squareup.okhttp3:logging-interceptor")
}

View file

@ -16,15 +16,18 @@
package io.element.android.libraries.core.meta
import okhttp3.logging.HttpLoggingInterceptor
data class BuildMeta(
val isDebug: Boolean,
val applicationName: String,
val applicationId: String,
val lowPrivacyLoggingEnabled: Boolean,
val versionName: String,
val gitRevision: String,
val gitRevisionDate: String,
val gitBranchName: String,
val flavorDescription: String,
val flavorShortDescription: String,
val isDebug: Boolean,
val applicationName: String,
val applicationId: String,
val lowPrivacyLoggingEnabled: Boolean,
val versionName: String,
val gitRevision: String,
val gitRevisionDate: String,
val gitBranchName: String,
val flavorDescription: String,
val flavorShortDescription: String,
val okHttpLoggingLevel: HttpLoggingInterceptor.Level,
)

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
}
android {
namespace = "io.element.android.libraries.network"
}
anvil {
generateDaggerFactories.set(true)
}
dependencies {
implementation(libs.dagger)
implementation(projects.libraries.core)
implementation(projects.libraries.di)
implementation(platform(libs.network.okhttp.bom))
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:logging-interceptor")
implementation(libs.network.retrofit)
implementation(libs.network.retrofit.converter.serialization)
implementation(libs.serialization.json)
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.network
import com.squareup.anvil.annotations.ContributesTo
import dagger.Module
import dagger.Provides
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn
import okhttp3.OkHttpClient
import okhttp3.Protocol
import io.element.android.libraries.network.interceptors.FormattedJsonHttpLogger
import java.util.concurrent.TimeUnit
import okhttp3.logging.HttpLoggingInterceptor
@Module
@ContributesTo(AppScope::class)
object NetworkModule {
@Provides
@JvmStatic
fun providesHttpLoggingInterceptor(buildMeta: BuildMeta): HttpLoggingInterceptor {
val logger = FormattedJsonHttpLogger(buildMeta.okHttpLoggingLevel)
val interceptor = HttpLoggingInterceptor(logger)
interceptor.level = buildMeta.okHttpLoggingLevel
return interceptor
}
@Provides
@SingleIn(AppScope::class)
fun providesOkHttpClient(
httpLoggingInterceptor: HttpLoggingInterceptor,
): OkHttpClient {
return OkHttpClient.Builder()
// workaround for #4669
.protocols(listOf(Protocol.HTTP_1_1))
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.addInterceptor(httpLoggingInterceptor)
.build()
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.network
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import io.element.android.libraries.core.uri.ensureTrailingSlash
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import javax.inject.Inject
class RetrofitFactory @Inject constructor(
private val okHttpClient: OkHttpClient,
) {
fun create(baseUrl: String): Retrofit {
val contentType = "application/json".toMediaType()
return Retrofit.Builder()
.baseUrl(baseUrl.ensureTrailingSlash())
.addConverterFactory(Json.asConverterFactory(contentType))
.client(okHttpClient)
.build()
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.network.interceptors
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import timber.log.Timber
internal class FormattedJsonHttpLogger(
private val level: HttpLoggingInterceptor.Level
) : HttpLoggingInterceptor.Logger {
companion object {
private const val INDENT_SPACE = 2
}
/**
* Log the message and try to log it again as a JSON formatted string.
* Note: it can consume a lot of memory but it is only in DEBUG mode.
*
* @param message
*/
@Synchronized
override fun log(message: String) {
Timber.v(message)
// Try to log formatted Json only if there is a chance that [message] contains Json.
// It can be only the case if we log the bodies of Http requests.
if (level != HttpLoggingInterceptor.Level.BODY) return
if (message.startsWith("{")) {
// JSON Detected
try {
val o = JSONObject(message)
logJson(o.toString(INDENT_SPACE))
} catch (e: JSONException) {
// Finally this is not a JSON string...
Timber.e(e)
}
} else if (message.startsWith("[")) {
// JSON Array detected
try {
val o = JSONArray(message)
logJson(o.toString(INDENT_SPACE))
} catch (e: JSONException) {
// Finally not JSON...
Timber.e(e)
}
}
// Else not a json string to log
}
private fun logJson(formattedJson: String) {
formattedJson
.lines()
.dropLastWhile { it.isEmpty() }
.forEach { Timber.v(it) }
}
}

View file

@ -55,6 +55,7 @@ fun DependencyHandlerScope.allLibrariesImpl() {
implementation(project(":libraries:designsystem"))
implementation(project(":libraries:matrix:impl"))
implementation(project(":libraries:matrixui"))
implementation(project(":libraries:network"))
implementation(project(":libraries:core"))
implementation(project(":libraries:architecture"))
implementation(project(":libraries:dateformatter:impl"))

View file

@ -49,6 +49,7 @@ include(":libraries:dateformatter:api")
include(":libraries:dateformatter:impl")
include(":libraries:dateformatter:test")
include(":libraries:elementresources")
include(":libraries:network")
include(":libraries:ui-strings")
include(":libraries:testtags")
include(":libraries:designsystem")