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.parsers.RoomStatusesParser
|
||||||
import be.digitalia.fosdem.utils.BackgroundWorkScope
|
import be.digitalia.fosdem.utils.BackgroundWorkScope
|
||||||
import be.digitalia.fosdem.utils.ByteCountSource
|
import be.digitalia.fosdem.utils.ByteCountSource
|
||||||
import be.digitalia.fosdem.utils.network.HttpUtils
|
import be.digitalia.fosdem.utils.network.HttpClient
|
||||||
import be.digitalia.fosdem.utils.network.HttpUtils.lastModified
|
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
@ -36,6 +35,7 @@ import kotlin.math.pow
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
class FosdemApi @Inject constructor(
|
class FosdemApi @Inject constructor(
|
||||||
|
private val httpClient: HttpClient,
|
||||||
private val scheduleDao: ScheduleDao,
|
private val scheduleDao: ScheduleDao,
|
||||||
private val alarmManager: FosdemAlarmManager
|
private val alarmManager: FosdemAlarmManager
|
||||||
) {
|
) {
|
||||||
|
@ -50,21 +50,19 @@ class FosdemApi @Inject constructor(
|
||||||
@MainThread
|
@MainThread
|
||||||
fun downloadSchedule(): Job {
|
fun downloadSchedule(): Job {
|
||||||
// Returns the download job in progress, if any
|
// Returns the download job in progress, if any
|
||||||
return downloadJob ?: run {
|
return downloadJob ?: BackgroundWorkScope.launch {
|
||||||
BackgroundWorkScope.launch {
|
|
||||||
downloadScheduleInternal()
|
downloadScheduleInternal()
|
||||||
downloadJob = null
|
downloadJob = null
|
||||||
}.also {
|
}.also {
|
||||||
downloadJob = it
|
downloadJob = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
private suspend fun downloadScheduleInternal() {
|
private suspend fun downloadScheduleInternal() {
|
||||||
_downloadScheduleState.value = LoadingState.Loading()
|
_downloadScheduleState.value = LoadingState.Loading()
|
||||||
val res = try {
|
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 length = body.contentLength()
|
||||||
val source = if (length > 0L) {
|
val source = if (length > 0L) {
|
||||||
// Broadcast the progression in percents, with a precision of 1/10 of the total file size
|
// 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)
|
val events = EventsParser().parse(source)
|
||||||
scheduleDao.storeSchedule(events, rawResponse.lastModified)
|
scheduleDao.storeSchedule(events, headers.get(HttpClient.LAST_MODIFIED_HEADER_NAME))
|
||||||
}
|
}
|
||||||
when (response) {
|
when (response) {
|
||||||
is HttpUtils.Response.NotModified -> DownloadScheduleResult.UpToDate // Nothing parsed, the result is up-to-date
|
is HttpClient.Response.NotModified -> DownloadScheduleResult.UpToDate // Nothing parsed, the result is up-to-date
|
||||||
is HttpUtils.Response.Success -> {
|
is HttpClient.Response.Success -> {
|
||||||
alarmManager.onScheduleRefreshed()
|
alarmManager.onScheduleRefreshed()
|
||||||
DownloadScheduleResult.Success(response.body)
|
DownloadScheduleResult.Success(response.body)
|
||||||
}
|
}
|
||||||
|
@ -139,7 +137,7 @@ class FosdemApi @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
nextRefreshDelay = try {
|
nextRefreshDelay = try {
|
||||||
val response = HttpUtils.get(FosdemUrls.rooms) { body, _ ->
|
val response = httpClient.get(FosdemUrls.rooms) { body, _ ->
|
||||||
RoomStatusesParser().parse(body.source())
|
RoomStatusesParser().parse(body.source())
|
||||||
}
|
}
|
||||||
now = SystemClock.elapsedRealtime()
|
now = SystemClock.elapsedRealtime()
|
||||||
|
|
|
@ -30,13 +30,13 @@ object DatabaseModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Named("Database")
|
@Named("Database")
|
||||||
fun providesSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
|
fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
|
||||||
return context.applicationContext.getSharedPreferences(DB_PREFS_FILE, Context.MODE_PRIVATE)
|
return context.applicationContext.getSharedPreferences(DB_PREFS_FILE, Context.MODE_PRIVATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun providesAppDatabase(@ApplicationContext context: Context): AppDatabase {
|
fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase {
|
||||||
val MIGRATION_1_2 = object : Migration(1, 2) {
|
val MIGRATION_1_2 = object : Migration(1, 2) {
|
||||||
override fun migrate(database: SupportSQLiteDatabase) = with(database) {
|
override fun migrate(database: SupportSQLiteDatabase) = with(database) {
|
||||||
// Events: make primary key and track_id not null
|
// Events: make primary key and track_id not null
|
||||||
|
@ -80,8 +80,8 @@ object DatabaseModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun providesScheduleDao(appDatabase: AppDatabase): ScheduleDao = appDatabase.scheduleDao
|
fun provideScheduleDao(appDatabase: AppDatabase): ScheduleDao = appDatabase.scheduleDao
|
||||||
|
|
||||||
@Provides
|
@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
|
package be.digitalia.fosdem.utils.network
|
||||||
|
|
||||||
import android.os.Build
|
import kotlinx.coroutines.Deferred
|
||||||
import be.digitalia.fosdem.utils.BackgroundWorkScope
|
|
||||||
import kotlinx.coroutines.CoroutineStart
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import okhttp3.Call
|
import okhttp3.Call
|
||||||
import okhttp3.Callback
|
import okhttp3.Callback
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.Headers
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import okhttp3.tls.HandshakeCertificates
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.util.concurrent.TimeUnit
|
import javax.inject.Inject
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.resumeWithException
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class to perform HTTP requests.
|
* High-level coroutines-based HTTP client.
|
||||||
*
|
*
|
||||||
* @author Christophe Beyls
|
* @author Christophe Beyls
|
||||||
*/
|
*/
|
||||||
object HttpUtils {
|
class HttpClient @Inject constructor(private val deferredCallFactory: @JvmSuppressWildcards Deferred<Call.Factory>) {
|
||||||
|
|
||||||
private const val DEFAULT_CONNECT_TIMEOUT = 10L
|
suspend fun <T> get(url: String, bodyParser: (body: ResponseBody, headers: Headers) -> T): Response.Success<T> {
|
||||||
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> {
|
|
||||||
return when (val response = get(url, null, bodyParser)) {
|
return when (val response = get(url, null, bodyParser)) {
|
||||||
// Can only receive NotModified if lastModified argument is non-null
|
// Can only receive NotModified if lastModified argument is non-null
|
||||||
is Response.NotModified -> throw IllegalStateException()
|
is Response.NotModified -> throw IllegalStateException()
|
||||||
|
@ -47,7 +31,7 @@ object HttpUtils {
|
||||||
/**
|
/**
|
||||||
* @param lastModified header value matching a previous "Last-Modified" response header.
|
* @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()
|
val requestBuilder = Request.Builder()
|
||||||
if (lastModified != null) {
|
if (lastModified != null) {
|
||||||
requestBuilder.header("If-Modified-Since", lastModified)
|
requestBuilder.header("If-Modified-Since", lastModified)
|
||||||
|
@ -56,10 +40,8 @@ object HttpUtils {
|
||||||
.url(url)
|
.url(url)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val client = deferredClient.await()
|
val call = deferredCallFactory.await().newCall(request)
|
||||||
|
|
||||||
return suspendCancellableCoroutine { continuation ->
|
return suspendCancellableCoroutine { continuation ->
|
||||||
val call = client.newCall(request)
|
|
||||||
call.enqueue(object : Callback {
|
call.enqueue(object : Callback {
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
continuation.resumeWithException(e)
|
continuation.resumeWithException(e)
|
||||||
|
@ -78,7 +60,7 @@ object HttpUtils {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
val parsedBody = checkNotNull(body).use { bodyParser(it, response) }
|
val parsedBody = checkNotNull(body).use { bodyParser(it, response.headers()) }
|
||||||
continuation.resume(Response.Success(parsedBody, response))
|
continuation.resume(Response.Success(parsedBody, response))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
continuation.resumeWithException(e)
|
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> {
|
sealed class Response<out T> {
|
||||||
object NotModified : Response<Nothing>()
|
object NotModified : Response<Nothing>()
|
||||||
class Success<T>(val body: T, val raw: okhttp3.Response) : Response<T>()
|
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 {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
kotlin_version = '1.5.10'
|
kotlin_version = '1.5.10'
|
||||||
hilt_version = '2.35.1'
|
hilt_version = '2.37'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
|
Loading…
Reference in a new issue