New core modules for :model, :navigation, :network, :prefs (#3147)

This commit is contained in:
Phil Oliver 2025-09-19 08:16:36 -04:00 committed by GitHub
parent bb707526f9
commit 0d2c1f1516
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
49 changed files with 228 additions and 212 deletions

View file

@ -0,0 +1,25 @@
/*
* 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/>.
*/
plugins {
alias(libs.plugins.meshtastic.android.library)
alias(libs.plugins.kover)
}
android { namespace = "org.meshtastic.core.model" }
dependencies {}

View file

@ -0,0 +1,26 @@
/*
* 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/>.
*/
plugins {
alias(libs.plugins.meshtastic.android.library)
alias(libs.plugins.meshtastic.kotlinx.serialization)
alias(libs.plugins.kover)
}
android { namespace = "org.meshtastic.core.navigation" }
dependencies {}

View file

@ -0,0 +1,152 @@
/*
* 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 org.meshtastic.core.navigation
import kotlinx.serialization.Serializable
const val DEEP_LINK_BASE_URI = "meshtastic://meshtastic"
interface Route
interface Graph : Route
object ChannelsRoutes {
@Serializable data object ChannelsGraph : Graph
@Serializable data object Channels : Route
}
object ConnectionsRoutes {
@Serializable data object ConnectionsGraph : Graph
@Serializable data object Connections : Route
}
object ContactsRoutes {
@Serializable data object ContactsGraph : Graph
@Serializable data object Contacts : Route
@Serializable data class Messages(val contactKey: String, val message: String = "") : Route
@Serializable data class Share(val message: String) : Route
@Serializable data object QuickChat : Route
}
object MapRoutes {
@Serializable data object Map : Route
}
object NodesRoutes {
@Serializable data object NodesGraph : Graph
@Serializable data object Nodes : Route
@Serializable data class NodeDetailGraph(val destNum: Int? = null) : Graph
@Serializable data class NodeDetail(val destNum: Int? = null) : Route
}
object NodeDetailRoutes {
@Serializable data object DeviceMetrics : Route
@Serializable data object NodeMap : Route
@Serializable data object PositionLog : Route
@Serializable data object EnvironmentMetrics : Route
@Serializable data object SignalMetrics : Route
@Serializable data object PowerMetrics : Route
@Serializable data object TracerouteLog : Route
@Serializable data object HostMetricsLog : Route
@Serializable data object PaxMetrics : Route
}
object SettingsRoutes {
@Serializable data class SettingsGraph(val destNum: Int? = null) : Graph
@Serializable data class Settings(val destNum: Int? = null) : Route
// region radio Config Routes
@Serializable data object User : Route
@Serializable data object ChannelConfig : Route
@Serializable data object Device : Route
@Serializable data object Position : Route
@Serializable data object Power : Route
@Serializable data object Network : Route
@Serializable data object Display : Route
@Serializable data object LoRa : Route
@Serializable data object Bluetooth : Route
@Serializable data object Security : Route
// endregion
// region module config routes
@Serializable data object MQTT : Route
@Serializable data object Serial : Route
@Serializable data object ExtNotification : Route
@Serializable data object StoreForward : Route
@Serializable data object RangeTest : Route
@Serializable data object Telemetry : Route
@Serializable data object CannedMessage : Route
@Serializable data object Audio : Route
@Serializable data object RemoteHardware : Route
@Serializable data object NeighborInfo : Route
@Serializable data object AmbientLighting : Route
@Serializable data object DetectionSensor : Route
@Serializable data object Paxcounter : Route
// endregion
// region advanced config routes
@Serializable data object CleanNodeDb : Route
@Serializable data object DebugPanel : Route
// endregion
}

View file

@ -0,0 +1,38 @@
/*
* 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/>.
*/
plugins {
alias(libs.plugins.meshtastic.android.library)
alias(libs.plugins.meshtastic.hilt)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.dokka)
alias(libs.plugins.kover)
alias(libs.plugins.protobuf)
alias(libs.plugins.ktorfit)
}
android {
buildFeatures { buildConfig = true }
namespace = "org.meshtastic.core.network"
}
dependencies {
implementation(libs.bundles.ktor)
implementation(libs.bundles.coil)
"googleImplementation"(libs.bundles.datadog)
implementation(libs.kotlinx.serialization.json)
}

View file

@ -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 org.meshtastic.core.network.di
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.meshtastic.core.network.BuildConfig
import org.meshtastic.core.network.model.NetworkDeviceHardware
import org.meshtastic.core.network.model.NetworkFirmwareReleases
import org.meshtastic.core.network.service.ApiService
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.")
}
}

View file

@ -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 org.meshtastic.core.network.di
import com.datadog.android.okhttp.DatadogEventListener
import com.datadog.android.okhttp.DatadogInterceptor
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 org.meshtastic.core.network.BuildConfig
import org.meshtastic.core.network.service.ApiService
import org.meshtastic.core.network.service.createApiService
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()
}
}

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,29 @@
/*
* 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 org.meshtastic.core.network
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.meshtastic.core.network.model.NetworkDeviceHardware
import org.meshtastic.core.network.service.ApiService
import javax.inject.Inject
class DeviceHardwareRemoteDataSource @Inject constructor(private val apiService: ApiService) {
suspend fun getAllDeviceHardware(): List<NetworkDeviceHardware> =
withContext(Dispatchers.IO) { apiService.getDeviceHardware() }
}

View file

@ -0,0 +1,29 @@
/*
* 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 org.meshtastic.core.network
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.meshtastic.core.network.model.NetworkFirmwareReleases
import org.meshtastic.core.network.service.ApiService
import javax.inject.Inject
class FirmwareReleaseRemoteDataSource @Inject constructor(private val apiService: ApiService) {
suspend fun getFirmwareReleases(): NetworkFirmwareReleases =
withContext(Dispatchers.IO) { apiService.getFirmwareReleases() }
}

View file

@ -0,0 +1,62 @@
/*
* 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 org.meshtastic.core.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 dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import org.meshtastic.core.network.BuildConfig
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 NetworkModule {
@Provides
@Singleton
fun provideImageLoader(okHttpClient: OkHttpClient, @ApplicationContext application: Context): ImageLoader {
val sharedOkHttp = okHttpClient.newBuilder().build()
return ImageLoader.Builder(context = application)
.components {
add(OkHttpNetworkFetcherFactory(callFactory = { sharedOkHttp }))
add(SvgDecoder.Factory())
}
.memoryCache {
MemoryCache.Builder().maxSizePercent(context = application, percent = MEMORY_CACHE_PERCENT).build()
}
.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()
}
}

View file

@ -0,0 +1,38 @@
/*
* 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 org.meshtastic.core.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,42 @@
/*
* 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 org.meshtastic.core.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,30 @@
/*
* 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 org.meshtastic.core.network.service
import de.jensklingenberg.ktorfit.http.GET
import org.meshtastic.core.network.model.NetworkDeviceHardware
import org.meshtastic.core.network.model.NetworkFirmwareReleases
interface ApiService {
@GET("resource/deviceHardware")
suspend fun getDeviceHardware(): List<NetworkDeviceHardware>
@GET("github/firmware/list")
suspend fun getFirmwareReleases(): NetworkFirmwareReleases
}

View file

@ -0,0 +1,25 @@
/*
* 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/>.
*/
plugins {
alias(libs.plugins.meshtastic.android.library)
alias(libs.plugins.kover)
}
android { namespace = "org.meshtastic.core.prefs" }
dependencies {}