1
0
Fork 0
mirror of https://github.com/MatomoCamp/matomocamp-companion-android.git synced 2024-09-19 16:13:46 +02:00

update Hilt to 2.37, inject HttpClient in FosdemApi, inject Call.Factory in HttpClient

This commit is contained in:
Christophe Beyls 2021-06-21 02:33:59 +02:00
parent cf41d38547
commit 71b1bcaf37
5 changed files with 88 additions and 61 deletions

View file

@ -18,8 +18,7 @@ import be.digitalia.fosdem.parsers.EventsParser
import be.digitalia.fosdem.parsers.RoomStatusesParser
import be.digitalia.fosdem.utils.BackgroundWorkScope
import be.digitalia.fosdem.utils.ByteCountSource
import be.digitalia.fosdem.utils.network.HttpUtils
import be.digitalia.fosdem.utils.network.HttpUtils.lastModified
import be.digitalia.fosdem.utils.network.HttpClient
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@ -36,6 +35,7 @@ import kotlin.math.pow
*/
@Singleton
class FosdemApi @Inject constructor(
private val httpClient: HttpClient,
private val scheduleDao: ScheduleDao,
private val alarmManager: FosdemAlarmManager
) {
@ -50,13 +50,11 @@ class FosdemApi @Inject constructor(
@MainThread
fun downloadSchedule(): Job {
// Returns the download job in progress, if any
return downloadJob ?: run {
BackgroundWorkScope.launch {
downloadScheduleInternal()
downloadJob = null
}.also {
downloadJob = it
}
return downloadJob ?: BackgroundWorkScope.launch {
downloadScheduleInternal()
downloadJob = null
}.also {
downloadJob = it
}
}
@ -64,7 +62,7 @@ class FosdemApi @Inject constructor(
private suspend fun downloadScheduleInternal() {
_downloadScheduleState.value = LoadingState.Loading()
val res = try {
val response = HttpUtils.get(FosdemUrls.schedule, scheduleDao.lastModifiedTag) { body, rawResponse ->
val response = httpClient.get(FosdemUrls.schedule, scheduleDao.lastModifiedTag) { body, headers ->
val length = body.contentLength()
val source = if (length > 0L) {
// Broadcast the progression in percents, with a precision of 1/10 of the total file size
@ -78,11 +76,11 @@ class FosdemApi @Inject constructor(
}
val events = EventsParser().parse(source)
scheduleDao.storeSchedule(events, rawResponse.lastModified)
scheduleDao.storeSchedule(events, headers.get(HttpClient.LAST_MODIFIED_HEADER_NAME))
}
when (response) {
is HttpUtils.Response.NotModified -> DownloadScheduleResult.UpToDate // Nothing parsed, the result is up-to-date
is HttpUtils.Response.Success -> {
is HttpClient.Response.NotModified -> DownloadScheduleResult.UpToDate // Nothing parsed, the result is up-to-date
is HttpClient.Response.Success -> {
alarmManager.onScheduleRefreshed()
DownloadScheduleResult.Success(response.body)
}
@ -139,7 +137,7 @@ class FosdemApi @Inject constructor(
}
nextRefreshDelay = try {
val response = HttpUtils.get(FosdemUrls.rooms) { body, _ ->
val response = httpClient.get(FosdemUrls.rooms) { body, _ ->
RoomStatusesParser().parse(body.source())
}
now = SystemClock.elapsedRealtime()

View file

@ -30,13 +30,13 @@ object DatabaseModule {
@Provides
@Named("Database")
fun providesSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
return context.applicationContext.getSharedPreferences(DB_PREFS_FILE, Context.MODE_PRIVATE)
}
@Provides
@Singleton
fun providesAppDatabase(@ApplicationContext context: Context): AppDatabase {
fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase {
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) = with(database) {
// Events: make primary key and track_id not null
@ -80,8 +80,8 @@ object DatabaseModule {
}
@Provides
fun providesScheduleDao(appDatabase: AppDatabase): ScheduleDao = appDatabase.scheduleDao
fun provideScheduleDao(appDatabase: AppDatabase): ScheduleDao = appDatabase.scheduleDao
@Provides
fun providesBookmarksDao(appDatabase: AppDatabase): BookmarksDao = appDatabase.bookmarksDao
fun provideBookmarksDao(appDatabase: AppDatabase): BookmarksDao = appDatabase.bookmarksDao
}

View file

@ -0,0 +1,56 @@
package be.digitalia.fosdem.inject
import android.os.Build
import be.digitalia.fosdem.utils.BackgroundWorkScope
import be.digitalia.fosdem.utils.network.Tls12SocketFactory
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import okhttp3.Call
import okhttp3.OkHttpClient
import okhttp3.tls.HandshakeCertificates
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
private const val DEFAULT_CONNECT_TIMEOUT = 10L
private const val DEFAULT_READ_TIMEOUT = 10L
@Provides
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.enableTls12()
.connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
.build()
}
private fun OkHttpClient.Builder.enableTls12(): OkHttpClient.Builder {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
val clientCertificates = HandshakeCertificates.Builder()
.addPlatformTrustedCertificates()
.build()
sslSocketFactory(
Tls12SocketFactory(clientCertificates.sslSocketFactory()),
clientCertificates.trustManager()
)
}
return this
}
/**
* Returns a Call.Factory lazily initialized on a background thread
*/
@Provides
@Singleton
fun provideDeferredCallFactory(lazyClient: dagger.Lazy<OkHttpClient>): Deferred<Call.Factory> {
return BackgroundWorkScope.async(Dispatchers.IO, CoroutineStart.LAZY) { lazyClient.get() }
}
}

View file

@ -1,42 +1,26 @@
package be.digitalia.fosdem.utils.network
import android.os.Build
import be.digitalia.fosdem.utils.BackgroundWorkScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Headers
import okhttp3.Request
import okhttp3.ResponseBody
import okhttp3.tls.HandshakeCertificates
import java.io.IOException
import java.net.HttpURLConnection
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
/**
* Utility class to perform HTTP requests.
* High-level coroutines-based HTTP client.
*
* @author Christophe Beyls
*/
object HttpUtils {
class HttpClient @Inject constructor(private val deferredCallFactory: @JvmSuppressWildcards Deferred<Call.Factory>) {
private const val DEFAULT_CONNECT_TIMEOUT = 10L
private const val DEFAULT_READ_TIMEOUT = 10L
private val deferredClient = BackgroundWorkScope.async(Dispatchers.IO, CoroutineStart.LAZY) {
OkHttpClient.Builder()
.enableTls12()
.connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
.build()
}
suspend fun <T> get(url: String, bodyParser: (body: ResponseBody, rawResponse: okhttp3.Response) -> T): Response.Success<T> {
suspend fun <T> get(url: String, bodyParser: (body: ResponseBody, headers: Headers) -> T): Response.Success<T> {
return when (val response = get(url, null, bodyParser)) {
// Can only receive NotModified if lastModified argument is non-null
is Response.NotModified -> throw IllegalStateException()
@ -47,19 +31,17 @@ object HttpUtils {
/**
* @param lastModified header value matching a previous "Last-Modified" response header.
*/
suspend fun <T> get(url: String, lastModified: String?, bodyParser: (body: ResponseBody, rawResponse: okhttp3.Response) -> T): Response<T> {
suspend fun <T> get(url: String, lastModified: String?, bodyParser: (body: ResponseBody, headers: Headers) -> T): Response<T> {
val requestBuilder = Request.Builder()
if (lastModified != null) {
requestBuilder.header("If-Modified-Since", lastModified)
}
val request = requestBuilder
.url(url)
.build()
val client = deferredClient.await()
.url(url)
.build()
val call = deferredCallFactory.await().newCall(request)
return suspendCancellableCoroutine { continuation ->
val call = client.newCall(request)
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
continuation.resumeWithException(e)
@ -78,7 +60,7 @@ object HttpUtils {
}
} else {
try {
val parsedBody = checkNotNull(body).use { bodyParser(it, response) }
val parsedBody = checkNotNull(body).use { bodyParser(it, response.headers()) }
continuation.resume(Response.Success(parsedBody, response))
} catch (e: Exception) {
continuation.resumeWithException(e)
@ -90,21 +72,12 @@ object HttpUtils {
}
}
val okhttp3.Response.lastModified: String?
get() = header("Last-Modified")
private fun OkHttpClient.Builder.enableTls12(): OkHttpClient.Builder {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
val clientCertificates = HandshakeCertificates.Builder()
.addPlatformTrustedCertificates()
.build()
sslSocketFactory(Tls12SocketFactory(clientCertificates.sslSocketFactory()), clientCertificates.trustManager())
}
return this
}
sealed class Response<out T> {
object NotModified : Response<Nothing>()
class Success<T>(val body: T, val raw: okhttp3.Response) : Response<T>()
}
companion object {
const val LAST_MODIFIED_HEADER_NAME = "Last-Modified"
}
}

View file

@ -2,7 +2,7 @@
buildscript {
ext {
kotlin_version = '1.5.10'
hilt_version = '2.35.1'
hilt_version = '2.37'
}
repositories {
google()