diff --git a/app/src/main/java/com/geeksville/mesh/repository/api/DeviceHardwareRepository.kt b/app/src/main/java/com/geeksville/mesh/repository/api/DeviceHardwareRepository.kt
index 273db1288..9dda196b8 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/api/DeviceHardwareRepository.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/api/DeviceHardwareRepository.kt
@@ -25,7 +25,6 @@ import com.geeksville.mesh.model.DeviceHardware
import com.geeksville.mesh.network.DeviceHardwareRemoteDataSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-import java.io.IOException
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@@ -69,8 +68,7 @@ constructor(
// 2. Fetch from remote API
runCatching {
debug("Fetching device hardware from remote API.")
- val remoteHardware =
- remoteDataSource.getAllDeviceHardware() ?: throw IOException("Empty response from server")
+ val remoteHardware = remoteDataSource.getAllDeviceHardware()
localDataSource.insertAllDeviceHardware(remoteHardware)
localDataSource.getByHwModel(hwModel)?.asExternalModel()
diff --git a/app/src/main/java/com/geeksville/mesh/repository/api/FirmwareReleaseRepository.kt b/app/src/main/java/com/geeksville/mesh/repository/api/FirmwareReleaseRepository.kt
index 5915af6eb..42316026f 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/api/FirmwareReleaseRepository.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/api/FirmwareReleaseRepository.kt
@@ -26,7 +26,6 @@ import com.geeksville.mesh.database.entity.asExternalModel
import com.geeksville.mesh.network.FirmwareReleaseRemoteDataSource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
-import java.io.IOException
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@@ -99,8 +98,7 @@ constructor(
val remoteFetchSuccess =
runCatching {
debug("Fetching fresh firmware releases from remote API.")
- val networkReleases =
- remoteDataSource.getFirmwareReleases() ?: throw IOException("Empty response from server")
+ val networkReleases = remoteDataSource.getFirmwareReleases()
// The API fetches all release types, so we cache them all at once.
localDataSource.insertFirmwareReleases(networkReleases.releases.stable, FirmwareReleaseType.STABLE)
diff --git a/build.gradle.kts b/build.gradle.kts
index 7eae6be01..1ebd08026 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -32,6 +32,7 @@ plugins {
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.kotlin.parcelize) apply false
alias(libs.plugins.kotlin.serialization) apply false
+ alias(libs.plugins.ktorfit) apply false
alias(libs.plugins.protobuf) apply false
alias(libs.plugins.secrets) apply false
alias(libs.plugins.dependency.analysis)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 0060855ef..962f0f396 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -22,8 +22,8 @@ hilt = "2.57.1"
maps-compose = "6.10.0"
# Networking
-okhttp = "5.1.0"
-retrofit = "3.0.0"
+ktor = "3.3.0"
+ktorfit = "2.6.4"
# Other
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" }
# Networking
-okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
-okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
-retrofit2 = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
-retrofit2-kotlin-serialization = { module = "com.squareup.retrofit2:converter-kotlinx-serialization", version.ref = "retrofit" }
+ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
+ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
+ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
+ktorfit = { module = "de.jensklingenberg.ktorfit:ktorfit-lib", version.ref = "ktorfit" }
+okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version = "5.1.0" }
# Testing
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"]
# 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
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-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
kover = { id = "org.jetbrains.kotlinx.kover", version = "0.9.1" }
+ktorfit = { id = "de.jensklingenberg.ktorfit", version.ref = "ktorfit" }
# Google
devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "devtools-ksp" }
diff --git a/network/build.gradle.kts b/network/build.gradle.kts
index 9b99d2a66..011a1d415 100644
--- a/network/build.gradle.kts
+++ b/network/build.gradle.kts
@@ -22,6 +22,7 @@ plugins {
alias(libs.plugins.dokka)
alias(libs.plugins.kover)
alias(libs.plugins.protobuf)
+ alias(libs.plugins.ktorfit)
}
android {
@@ -30,7 +31,7 @@ android {
}
dependencies {
- implementation(libs.bundles.retrofit)
+ implementation(libs.bundles.ktor)
implementation(libs.bundles.coil)
"googleImplementation"(libs.bundles.datadog)
implementation(libs.kotlinx.serialization.json)
diff --git a/network/src/fdroid/java/com/geeksville/mesh/network/di/FDroidNetworkModule.kt b/network/src/fdroid/java/com/geeksville/mesh/network/di/FDroidNetworkModule.kt
new file mode 100644
index 000000000..e422dd6cb
--- /dev/null
+++ b/network/src/fdroid/java/com/geeksville/mesh/network/di/FDroidNetworkModule.kt
@@ -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 .
+ */
+
+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 =
+ 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.")
+ }
+}
diff --git a/network/src/fdroid/java/com/geeksville/mesh/network/retrofit/NoOpApiService.kt b/network/src/fdroid/java/com/geeksville/mesh/network/retrofit/NoOpApiService.kt
deleted file mode 100644
index 0e597524c..000000000
--- a/network/src/fdroid/java/com/geeksville/mesh/network/retrofit/NoOpApiService.kt
+++ /dev/null
@@ -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 .
- */
-
-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> {
- return Response.error(ERROR_NO_OP, "Not Found".toResponseBody(null))
- }
-
- override suspend fun getFirmwareReleases(): Response {
- return Response.error(ERROR_NO_OP, "Not Found".toResponseBody(null))
- }
-}
diff --git a/network/src/google/java/com/geeksville/mesh/network/di/ApiModule.kt b/network/src/google/java/com/geeksville/mesh/network/di/ApiModule.kt
deleted file mode 100644
index 7661d355a..000000000
--- a/network/src/google/java/com/geeksville/mesh/network/di/ApiModule.kt
+++ /dev/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 .
- */
-
-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()
- }
-}
diff --git a/network/src/google/java/com/geeksville/mesh/network/di/GoogleNetworkModule.kt b/network/src/google/java/com/geeksville/mesh/network/di/GoogleNetworkModule.kt
new file mode 100644
index 000000000..5110be3dc
--- /dev/null
+++ b/network/src/google/java/com/geeksville/mesh/network/di/GoogleNetworkModule.kt
@@ -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 .
+ */
+
+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()
+ }
+}
diff --git a/network/src/main/java/com/geeksville/mesh/network/DeviceHardwareRemoteDataSource.kt b/network/src/main/java/com/geeksville/mesh/network/DeviceHardwareRemoteDataSource.kt
index 932abe038..b233b258a 100644
--- a/network/src/main/java/com/geeksville/mesh/network/DeviceHardwareRemoteDataSource.kt
+++ b/network/src/main/java/com/geeksville/mesh/network/DeviceHardwareRemoteDataSource.kt
@@ -18,15 +18,12 @@
package com.geeksville.mesh.network
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.withContext
import javax.inject.Inject
-class DeviceHardwareRemoteDataSource @Inject constructor(
- private val apiService: ApiService,
-) {
- suspend fun getAllDeviceHardware(): List? = withContext(Dispatchers.IO) {
- apiService.getDeviceHardware().body()
- }
+class DeviceHardwareRemoteDataSource @Inject constructor(private val apiService: ApiService) {
+ suspend fun getAllDeviceHardware(): List =
+ withContext(Dispatchers.IO) { apiService.getDeviceHardware() }
}
diff --git a/network/src/main/java/com/geeksville/mesh/network/FirmwareReleaseRemoteDataSource.kt b/network/src/main/java/com/geeksville/mesh/network/FirmwareReleaseRemoteDataSource.kt
index 7d3f2ec08..0dda14797 100644
--- a/network/src/main/java/com/geeksville/mesh/network/FirmwareReleaseRemoteDataSource.kt
+++ b/network/src/main/java/com/geeksville/mesh/network/FirmwareReleaseRemoteDataSource.kt
@@ -18,15 +18,12 @@
package com.geeksville.mesh.network
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.withContext
import javax.inject.Inject
-class FirmwareReleaseRemoteDataSource @Inject constructor(
- private val apiService: ApiService,
-) {
- suspend fun getFirmwareReleases(): NetworkFirmwareReleases? = withContext(Dispatchers.IO) {
- apiService.getFirmwareReleases().body()
- }
+class FirmwareReleaseRemoteDataSource @Inject constructor(private val apiService: ApiService) {
+ suspend fun getFirmwareReleases(): NetworkFirmwareReleases =
+ withContext(Dispatchers.IO) { apiService.getFirmwareReleases() }
}
diff --git a/network/src/fdroid/java/com/geeksville/mesh/network/di/ApiModule.kt b/network/src/main/java/com/geeksville/mesh/network/di/NetworkModule.kt
similarity index 62%
rename from network/src/fdroid/java/com/geeksville/mesh/network/di/ApiModule.kt
rename to network/src/main/java/com/geeksville/mesh/network/di/NetworkModule.kt
index 34b8b6be2..15453554e 100644
--- a/network/src/fdroid/java/com/geeksville/mesh/network/di/ApiModule.kt
+++ b/network/src/main/java/com/geeksville/mesh/network/di/NetworkModule.kt
@@ -27,8 +27,6 @@ import coil3.svg.SvgDecoder
import coil3.util.DebugLogger
import coil3.util.Logger
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.Provides
import dagger.hilt.InstallIn
@@ -42,40 +40,23 @@ private const val MEMORY_CACHE_PERCENT = 0.25
@InstallIn(SingletonComponent::class)
@Module
-class ApiModule {
+class NetworkModule {
@Provides
@Singleton
- fun provideApiService(): ApiService {
- return NoOpApiService()
- }
-
- @Provides
- @Singleton
- fun imageLoader(
- httpClient: OkHttpClient,
- @ApplicationContext application: Context,
- ): ImageLoader {
- val sharedOkHttp = httpClient.newBuilder().build()
- return ImageLoader.Builder(application)
+ fun provideImageLoader(okHttpClient: OkHttpClient, @ApplicationContext application: Context): ImageLoader {
+ val sharedOkHttp = okHttpClient.newBuilder().build()
+ return ImageLoader.Builder(context = application)
.components {
- add(
- OkHttpNetworkFetcherFactory({ sharedOkHttp })
- )
+ add(OkHttpNetworkFetcherFactory(callFactory = { sharedOkHttp }))
add(SvgDecoder.Factory())
}
.memoryCache {
- MemoryCache.Builder()
- .maxSizePercent(application, MEMORY_CACHE_PERCENT)
- .build()
+ MemoryCache.Builder().maxSizePercent(context = application, percent = MEMORY_CACHE_PERCENT).build()
}
- .diskCache {
- DiskCache.Builder()
- .maxSizePercent(DISK_CACHE_PERCENT)
- .build()
- }
- .logger(if (BuildConfig.DEBUG) DebugLogger(Logger.Level.Verbose) else null)
- .crossfade(true)
+ .diskCache { DiskCache.Builder().maxSizePercent(percent = DISK_CACHE_PERCENT).build() }
+ .logger(logger = if (BuildConfig.DEBUG) DebugLogger(minLevel = Logger.Level.Verbose) else null)
+ .crossfade(enable = true)
.build()
}
}
diff --git a/network/src/main/java/com/geeksville/mesh/network/retrofit/ApiService.kt b/network/src/main/java/com/geeksville/mesh/network/service/ApiService.kt
similarity index 76%
rename from network/src/main/java/com/geeksville/mesh/network/retrofit/ApiService.kt
rename to network/src/main/java/com/geeksville/mesh/network/service/ApiService.kt
index daf513411..f1011e433 100644
--- a/network/src/main/java/com/geeksville/mesh/network/retrofit/ApiService.kt
+++ b/network/src/main/java/com/geeksville/mesh/network/service/ApiService.kt
@@ -15,17 +15,16 @@
* along with this program. If not, see .
*/
-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.NetworkFirmwareReleases
-import retrofit2.Response
-import retrofit2.http.GET
+import de.jensklingenberg.ktorfit.http.GET
interface ApiService {
@GET("resource/deviceHardware")
- suspend fun getDeviceHardware(): Response>
+ suspend fun getDeviceHardware(): List
- @GET("/github/firmware/list")
- suspend fun getFirmwareReleases(): Response
+ @GET("github/firmware/list")
+ suspend fun getFirmwareReleases(): NetworkFirmwareReleases
}