feat: network module (#1905)

This commit is contained in:
James Rich
2025-05-22 08:30:08 -05:00
committed by GitHub
parent 520d058546
commit 02bb3f02e4
80 changed files with 2165 additions and 15032 deletions

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@@ -0,0 +1,32 @@
/*
* 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
import com.geeksville.mesh.network.model.NetworkDeviceHardware
import com.geeksville.mesh.network.retrofit.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<NetworkDeviceHardware> = withContext(Dispatchers.IO) {
apiService.getDeviceHardware().body() ?: emptyList()
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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
import com.geeksville.mesh.network.model.NetworkFirmwareReleases
import com.geeksville.mesh.network.retrofit.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() ?: NetworkFirmwareReleases()
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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.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)
}
}
return OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.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()
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class NetworkDeviceHardware(
@SerialName("activelySupported")
val activelySupported: Boolean = false,
@SerialName("architecture")
val architecture: String = "",
@SerialName("displayName")
val displayName: String = "",
@SerialName("hasInkHud")
val hasInkHud: Boolean? = null,
@SerialName("hasMui")
val hasMui: Boolean? = null,
@SerialName("hwModel")
val hwModel: Int = 0,
@SerialName("hwModelSlug")
val hwModelSlug: String = "",
@SerialName("images")
val images: List<String>? = null,
@SerialName("partitionScheme")
val partitionScheme: String? = null,
@SerialName("platformioTarget")
val platformioTarget: String = "",
@SerialName("requiresDfu")
val requiresDfu: Boolean? = null,
@SerialName("supportLevel")
val supportLevel: Int? = null,
@SerialName("tags")
val tags: List<String>? = null
)

View File

@@ -0,0 +1,51 @@
/*
* 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.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class NetworkFirmwareRelease(
@SerialName("id")
val id: String = "",
@SerialName("page_url")
val pageUrl: String = "",
@SerialName("release_notes")
val releaseNotes: String = "",
@SerialName("title")
val title: String = "",
@SerialName("zip_url")
val zipUrl: String = ""
)
@Serializable
data class Releases(
@SerialName("alpha")
val alpha: List<NetworkFirmwareRelease> = listOf(),
@SerialName("stable")
val stable: List<NetworkFirmwareRelease> = listOf()
)
@Serializable
data class NetworkFirmwareReleases(
@SerialName("pullRequests")
val pullRequests: List<NetworkFirmwareRelease> = listOf(),
@SerialName("releases")
val releases: Releases = Releases()
)

View File

@@ -0,0 +1,35 @@
/*
* 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 retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Query
interface ApiService {
@GET(".")
suspend fun checkDeviceRegistration(@Query("deviceId") deviceId: String): Response<Unit>
@GET("resource/deviceHardware")
suspend fun getDeviceHardware(): Response<List<NetworkDeviceHardware>>
@GET("/github/firmware/list")
suspend fun getFirmwareReleases(): Response<NetworkFirmwareReleases>
}