mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Use Ktor/Ktorfit for API calls (#3122)
This commit is contained in:
parent
d600d182b5
commit
bec5dac9d4
13 changed files with 173 additions and 217 deletions
|
|
@ -25,7 +25,6 @@ import com.geeksville.mesh.model.DeviceHardware
|
||||||
import com.geeksville.mesh.network.DeviceHardwareRemoteDataSource
|
import com.geeksville.mesh.network.DeviceHardwareRemoteDataSource
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.IOException
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
@ -69,8 +68,7 @@ constructor(
|
||||||
// 2. Fetch from remote API
|
// 2. Fetch from remote API
|
||||||
runCatching {
|
runCatching {
|
||||||
debug("Fetching device hardware from remote API.")
|
debug("Fetching device hardware from remote API.")
|
||||||
val remoteHardware =
|
val remoteHardware = remoteDataSource.getAllDeviceHardware()
|
||||||
remoteDataSource.getAllDeviceHardware() ?: throw IOException("Empty response from server")
|
|
||||||
|
|
||||||
localDataSource.insertAllDeviceHardware(remoteHardware)
|
localDataSource.insertAllDeviceHardware(remoteHardware)
|
||||||
localDataSource.getByHwModel(hwModel)?.asExternalModel()
|
localDataSource.getByHwModel(hwModel)?.asExternalModel()
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import com.geeksville.mesh.database.entity.asExternalModel
|
||||||
import com.geeksville.mesh.network.FirmwareReleaseRemoteDataSource
|
import com.geeksville.mesh.network.FirmwareReleaseRemoteDataSource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import java.io.IOException
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
@ -99,8 +98,7 @@ constructor(
|
||||||
val remoteFetchSuccess =
|
val remoteFetchSuccess =
|
||||||
runCatching {
|
runCatching {
|
||||||
debug("Fetching fresh firmware releases from remote API.")
|
debug("Fetching fresh firmware releases from remote API.")
|
||||||
val networkReleases =
|
val networkReleases = remoteDataSource.getFirmwareReleases()
|
||||||
remoteDataSource.getFirmwareReleases() ?: throw IOException("Empty response from server")
|
|
||||||
|
|
||||||
// The API fetches all release types, so we cache them all at once.
|
// The API fetches all release types, so we cache them all at once.
|
||||||
localDataSource.insertFirmwareReleases(networkReleases.releases.stable, FirmwareReleaseType.STABLE)
|
localDataSource.insertFirmwareReleases(networkReleases.releases.stable, FirmwareReleaseType.STABLE)
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ plugins {
|
||||||
alias(libs.plugins.kotlin.jvm) apply false
|
alias(libs.plugins.kotlin.jvm) apply false
|
||||||
alias(libs.plugins.kotlin.parcelize) apply false
|
alias(libs.plugins.kotlin.parcelize) apply false
|
||||||
alias(libs.plugins.kotlin.serialization) apply false
|
alias(libs.plugins.kotlin.serialization) apply false
|
||||||
|
alias(libs.plugins.ktorfit) apply false
|
||||||
alias(libs.plugins.protobuf) apply false
|
alias(libs.plugins.protobuf) apply false
|
||||||
alias(libs.plugins.secrets) apply false
|
alias(libs.plugins.secrets) apply false
|
||||||
alias(libs.plugins.dependency.analysis)
|
alias(libs.plugins.dependency.analysis)
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ hilt = "2.57.1"
|
||||||
maps-compose = "6.10.0"
|
maps-compose = "6.10.0"
|
||||||
|
|
||||||
# Networking
|
# Networking
|
||||||
okhttp = "5.1.0"
|
ktor = "3.3.0"
|
||||||
retrofit = "3.0.0"
|
ktorfit = "2.6.4"
|
||||||
|
|
||||||
# Other
|
# Other
|
||||||
coil = "3.3.0"
|
coil = "3.3.0"
|
||||||
|
|
@ -115,10 +115,11 @@ kotlinx-coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-
|
||||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.9.0" }
|
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.9.0" }
|
||||||
|
|
||||||
# Networking
|
# Networking
|
||||||
okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
|
||||||
okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
|
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
|
||||||
retrofit2 = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
|
||||||
retrofit2-kotlin-serialization = { module = "com.squareup.retrofit2:converter-kotlinx-serialization", version.ref = "retrofit" }
|
ktorfit = { module = "de.jensklingenberg.ktorfit:ktorfit-lib", version.ref = "ktorfit" }
|
||||||
|
okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version = "5.1.0" }
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
espresso-core = { module = "androidx.test.espresso:espresso-core", version = "3.7.0" }
|
espresso-core = { module = "androidx.test.espresso:espresso-core", version = "3.7.0" }
|
||||||
|
|
@ -189,7 +190,7 @@ firebase = ["firebase-analytics", "firebase-crashlytics", "firebase-performance"
|
||||||
maps-compose = ["location-services", "maps-compose", "maps-compose-utils", "maps-compose-widgets"]
|
maps-compose = ["location-services", "maps-compose", "maps-compose-utils", "maps-compose-widgets"]
|
||||||
|
|
||||||
# Networking
|
# Networking
|
||||||
retrofit = ["retrofit2", "retrofit2-kotlin-serialization", "okhttp3", "okhttp3-logging-interceptor"]
|
ktor = ["ktor-client-content-negotiation", "ktor-client-okhttp", "ktor-serialization-kotlinx-json", "ktorfit", "okhttp3-logging-interceptor"]
|
||||||
|
|
||||||
# Other
|
# Other
|
||||||
coil = ["coil", "coil-network-core", "coil-network-okhttp", "coil-svg"]
|
coil = ["coil", "coil-network-core", "coil-network-okhttp", "coil-svg"]
|
||||||
|
|
@ -224,6 +225,7 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
|
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
|
||||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||||
kover = { id = "org.jetbrains.kotlinx.kover", version = "0.9.1" }
|
kover = { id = "org.jetbrains.kotlinx.kover", version = "0.9.1" }
|
||||||
|
ktorfit = { id = "de.jensklingenberg.ktorfit", version.ref = "ktorfit" }
|
||||||
|
|
||||||
# Google
|
# Google
|
||||||
devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "devtools-ksp" }
|
devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "devtools-ksp" }
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ plugins {
|
||||||
alias(libs.plugins.dokka)
|
alias(libs.plugins.dokka)
|
||||||
alias(libs.plugins.kover)
|
alias(libs.plugins.kover)
|
||||||
alias(libs.plugins.protobuf)
|
alias(libs.plugins.protobuf)
|
||||||
|
alias(libs.plugins.ktorfit)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
@ -30,7 +31,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.bundles.retrofit)
|
implementation(libs.bundles.ktor)
|
||||||
implementation(libs.bundles.coil)
|
implementation(libs.bundles.coil)
|
||||||
"googleImplementation"(libs.bundles.datadog)
|
"googleImplementation"(libs.bundles.datadog)
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Meshtastic LLC
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.geeksville.mesh.network.di
|
||||||
|
|
||||||
|
import com.geeksville.mesh.network.BuildConfig
|
||||||
|
import com.geeksville.mesh.network.model.NetworkDeviceHardware
|
||||||
|
import com.geeksville.mesh.network.model.NetworkFirmwareReleases
|
||||||
|
import com.geeksville.mesh.network.service.ApiService
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
@Module
|
||||||
|
class FDroidNetworkModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder()
|
||||||
|
.addInterceptor(
|
||||||
|
interceptor =
|
||||||
|
HttpLoggingInterceptor().apply {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
setLevel(HttpLoggingInterceptor.Level.BODY)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideApiService(): ApiService = object : ApiService {
|
||||||
|
override suspend fun getDeviceHardware(): List<NetworkDeviceHardware> =
|
||||||
|
throw NotImplementedError("API calls to getDeviceHardware are not supported on Fdroid builds.")
|
||||||
|
|
||||||
|
override suspend fun getFirmwareReleases(): NetworkFirmwareReleases =
|
||||||
|
throw NotImplementedError("API calls to getFirmwareReleases are not supported on Fdroid builds.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025 Meshtastic LLC
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.geeksville.mesh.network.retrofit
|
|
||||||
|
|
||||||
import com.geeksville.mesh.network.model.NetworkDeviceHardware
|
|
||||||
import com.geeksville.mesh.network.model.NetworkFirmwareReleases
|
|
||||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
|
||||||
import retrofit2.Response
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
private const val ERROR_NO_OP = 420
|
|
||||||
@Singleton
|
|
||||||
class NoOpApiService@Inject constructor() : ApiService {
|
|
||||||
override suspend fun getDeviceHardware(): Response<List<NetworkDeviceHardware>> {
|
|
||||||
return Response.error(ERROR_NO_OP, "Not Found".toResponseBody(null))
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getFirmwareReleases(): Response<NetworkFirmwareReleases> {
|
|
||||||
return Response.error(ERROR_NO_OP, "Not Found".toResponseBody(null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025 Meshtastic LLC
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.geeksville.mesh.network.di
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import coil3.ImageLoader
|
|
||||||
import coil3.disk.DiskCache
|
|
||||||
import coil3.memory.MemoryCache
|
|
||||||
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
|
||||||
import coil3.request.crossfade
|
|
||||||
import coil3.svg.SvgDecoder
|
|
||||||
import coil3.util.DebugLogger
|
|
||||||
import coil3.util.Logger
|
|
||||||
import com.datadog.android.okhttp.DatadogEventListener
|
|
||||||
import com.datadog.android.okhttp.DatadogInterceptor
|
|
||||||
import com.geeksville.mesh.network.BuildConfig
|
|
||||||
import com.geeksville.mesh.network.retrofit.ApiService
|
|
||||||
import dagger.Module
|
|
||||||
import dagger.Provides
|
|
||||||
import dagger.hilt.InstallIn
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import dagger.hilt.components.SingletonComponent
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
|
||||||
import retrofit2.Retrofit
|
|
||||||
import retrofit2.converter.kotlinx.serialization.asConverterFactory
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
private const val DISK_CACHE_PERCENT = 0.02
|
|
||||||
private const val MEMORY_CACHE_PERCENT = 0.25
|
|
||||||
@InstallIn(SingletonComponent::class)
|
|
||||||
@Module
|
|
||||||
class ApiModule {
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideOkHttpClient(): OkHttpClient {
|
|
||||||
|
|
||||||
val loggingInterceptor = HttpLoggingInterceptor().apply {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
setLevel(HttpLoggingInterceptor.Level.BODY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val tracedHosts = listOf("meshtastic.org")
|
|
||||||
return OkHttpClient.Builder()
|
|
||||||
.addInterceptor(loggingInterceptor)
|
|
||||||
.addInterceptor(DatadogInterceptor.Builder(tracedHosts).build())
|
|
||||||
.eventListenerFactory(DatadogEventListener.Factory())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideRetrofit(
|
|
||||||
okHttpClient: OkHttpClient
|
|
||||||
): Retrofit {
|
|
||||||
return Retrofit.Builder()
|
|
||||||
.baseUrl("https://api.meshtastic.org/") // Replace with your base URL
|
|
||||||
.addConverterFactory(
|
|
||||||
Json.asConverterFactory(
|
|
||||||
"application/json; charset=UTF8".toMediaType()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.client(okHttpClient)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideApiService(retrofit: Retrofit): ApiService {
|
|
||||||
return retrofit.create(ApiService::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun imageLoader(
|
|
||||||
httpClient: OkHttpClient,
|
|
||||||
@ApplicationContext application: Context,
|
|
||||||
): ImageLoader {
|
|
||||||
val sharedOkHttp = httpClient.newBuilder().build()
|
|
||||||
return ImageLoader.Builder(application)
|
|
||||||
.components {
|
|
||||||
add(
|
|
||||||
OkHttpNetworkFetcherFactory({ sharedOkHttp })
|
|
||||||
)
|
|
||||||
add(SvgDecoder.Factory())
|
|
||||||
}
|
|
||||||
.memoryCache {
|
|
||||||
MemoryCache.Builder()
|
|
||||||
.maxSizePercent(application, MEMORY_CACHE_PERCENT)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
.diskCache {
|
|
||||||
DiskCache.Builder()
|
|
||||||
.maxSizePercent(DISK_CACHE_PERCENT)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
.logger(if (BuildConfig.DEBUG) DebugLogger(Logger.Level.Verbose) else null)
|
|
||||||
.crossfade(true)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Meshtastic LLC
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.geeksville.mesh.network.di
|
||||||
|
|
||||||
|
import com.datadog.android.okhttp.DatadogEventListener
|
||||||
|
import com.datadog.android.okhttp.DatadogInterceptor
|
||||||
|
import com.geeksville.mesh.network.BuildConfig
|
||||||
|
import com.geeksville.mesh.network.service.ApiService
|
||||||
|
import com.geeksville.mesh.network.service.createApiService
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import de.jensklingenberg.ktorfit.Ktorfit
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.engine.okhttp.OkHttp
|
||||||
|
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||||
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
@Module
|
||||||
|
class GoogleNetworkModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder()
|
||||||
|
.addInterceptor(
|
||||||
|
interceptor =
|
||||||
|
HttpLoggingInterceptor().apply {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
setLevel(HttpLoggingInterceptor.Level.BODY)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.addInterceptor(interceptor = DatadogInterceptor.Builder(tracedHosts = listOf("meshtastic.org")).build())
|
||||||
|
.eventListenerFactory(eventListenerFactory = DatadogEventListener.Factory())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideHttpClient(okHttpClient: OkHttpClient): HttpClient = HttpClient(engineFactory = OkHttp) {
|
||||||
|
engine { preconfigured = okHttpClient }
|
||||||
|
|
||||||
|
install(plugin = ContentNegotiation) {
|
||||||
|
json(
|
||||||
|
Json {
|
||||||
|
isLenient = true
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideApiService(httpClient: HttpClient): ApiService {
|
||||||
|
val ktorfit = Ktorfit.Builder().baseUrl(url = "https://api.meshtastic.org/").httpClient(httpClient).build()
|
||||||
|
return ktorfit.createApiService()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,15 +18,12 @@
|
||||||
package com.geeksville.mesh.network
|
package com.geeksville.mesh.network
|
||||||
|
|
||||||
import com.geeksville.mesh.network.model.NetworkDeviceHardware
|
import com.geeksville.mesh.network.model.NetworkDeviceHardware
|
||||||
import com.geeksville.mesh.network.retrofit.ApiService
|
import com.geeksville.mesh.network.service.ApiService
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DeviceHardwareRemoteDataSource @Inject constructor(
|
class DeviceHardwareRemoteDataSource @Inject constructor(private val apiService: ApiService) {
|
||||||
private val apiService: ApiService,
|
suspend fun getAllDeviceHardware(): List<NetworkDeviceHardware> =
|
||||||
) {
|
withContext(Dispatchers.IO) { apiService.getDeviceHardware() }
|
||||||
suspend fun getAllDeviceHardware(): List<NetworkDeviceHardware>? = withContext(Dispatchers.IO) {
|
|
||||||
apiService.getDeviceHardware().body()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,12 @@
|
||||||
package com.geeksville.mesh.network
|
package com.geeksville.mesh.network
|
||||||
|
|
||||||
import com.geeksville.mesh.network.model.NetworkFirmwareReleases
|
import com.geeksville.mesh.network.model.NetworkFirmwareReleases
|
||||||
import com.geeksville.mesh.network.retrofit.ApiService
|
import com.geeksville.mesh.network.service.ApiService
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class FirmwareReleaseRemoteDataSource @Inject constructor(
|
class FirmwareReleaseRemoteDataSource @Inject constructor(private val apiService: ApiService) {
|
||||||
private val apiService: ApiService,
|
suspend fun getFirmwareReleases(): NetworkFirmwareReleases =
|
||||||
) {
|
withContext(Dispatchers.IO) { apiService.getFirmwareReleases() }
|
||||||
suspend fun getFirmwareReleases(): NetworkFirmwareReleases? = withContext(Dispatchers.IO) {
|
|
||||||
apiService.getFirmwareReleases().body()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ import coil3.svg.SvgDecoder
|
||||||
import coil3.util.DebugLogger
|
import coil3.util.DebugLogger
|
||||||
import coil3.util.Logger
|
import coil3.util.Logger
|
||||||
import com.geeksville.mesh.network.BuildConfig
|
import com.geeksville.mesh.network.BuildConfig
|
||||||
import com.geeksville.mesh.network.retrofit.ApiService
|
|
||||||
import com.geeksville.mesh.network.retrofit.NoOpApiService
|
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
|
@ -42,40 +40,23 @@ private const val MEMORY_CACHE_PERCENT = 0.25
|
||||||
|
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
@Module
|
@Module
|
||||||
class ApiModule {
|
class NetworkModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideApiService(): ApiService {
|
fun provideImageLoader(okHttpClient: OkHttpClient, @ApplicationContext application: Context): ImageLoader {
|
||||||
return NoOpApiService()
|
val sharedOkHttp = okHttpClient.newBuilder().build()
|
||||||
}
|
return ImageLoader.Builder(context = application)
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun imageLoader(
|
|
||||||
httpClient: OkHttpClient,
|
|
||||||
@ApplicationContext application: Context,
|
|
||||||
): ImageLoader {
|
|
||||||
val sharedOkHttp = httpClient.newBuilder().build()
|
|
||||||
return ImageLoader.Builder(application)
|
|
||||||
.components {
|
.components {
|
||||||
add(
|
add(OkHttpNetworkFetcherFactory(callFactory = { sharedOkHttp }))
|
||||||
OkHttpNetworkFetcherFactory({ sharedOkHttp })
|
|
||||||
)
|
|
||||||
add(SvgDecoder.Factory())
|
add(SvgDecoder.Factory())
|
||||||
}
|
}
|
||||||
.memoryCache {
|
.memoryCache {
|
||||||
MemoryCache.Builder()
|
MemoryCache.Builder().maxSizePercent(context = application, percent = MEMORY_CACHE_PERCENT).build()
|
||||||
.maxSizePercent(application, MEMORY_CACHE_PERCENT)
|
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
.diskCache {
|
.diskCache { DiskCache.Builder().maxSizePercent(percent = DISK_CACHE_PERCENT).build() }
|
||||||
DiskCache.Builder()
|
.logger(logger = if (BuildConfig.DEBUG) DebugLogger(minLevel = Logger.Level.Verbose) else null)
|
||||||
.maxSizePercent(DISK_CACHE_PERCENT)
|
.crossfade(enable = true)
|
||||||
.build()
|
|
||||||
}
|
|
||||||
.logger(if (BuildConfig.DEBUG) DebugLogger(Logger.Level.Verbose) else null)
|
|
||||||
.crossfade(true)
|
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -15,17 +15,16 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.geeksville.mesh.network.retrofit
|
package com.geeksville.mesh.network.service
|
||||||
|
|
||||||
import com.geeksville.mesh.network.model.NetworkDeviceHardware
|
import com.geeksville.mesh.network.model.NetworkDeviceHardware
|
||||||
import com.geeksville.mesh.network.model.NetworkFirmwareReleases
|
import com.geeksville.mesh.network.model.NetworkFirmwareReleases
|
||||||
import retrofit2.Response
|
import de.jensklingenberg.ktorfit.http.GET
|
||||||
import retrofit2.http.GET
|
|
||||||
|
|
||||||
interface ApiService {
|
interface ApiService {
|
||||||
@GET("resource/deviceHardware")
|
@GET("resource/deviceHardware")
|
||||||
suspend fun getDeviceHardware(): Response<List<NetworkDeviceHardware>>
|
suspend fun getDeviceHardware(): List<NetworkDeviceHardware>
|
||||||
|
|
||||||
@GET("/github/firmware/list")
|
@GET("github/firmware/list")
|
||||||
suspend fun getFirmwareReleases(): Response<NetworkFirmwareReleases>
|
suspend fun getFirmwareReleases(): NetworkFirmwareReleases
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue