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:
parent
cf41d38547
commit
71b1bcaf37
5 changed files with 88 additions and 61 deletions
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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() }
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.5.10'
|
||||
hilt_version = '2.35.1'
|
||||
hilt_version = '2.37'
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
|
|
Loading…
Reference in a new issue