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

pull/3149/head
Phil Oliver 2025-09-19 08:16:36 -04:00 zatwierdzone przez GitHub
rodzic bb707526f9
commit 0d2c1f1516
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
49 zmienionych plików z 228 dodań i 212 usunięć

5
.gitignore vendored
Wyświetl plik

@ -10,12 +10,14 @@
.gradle
/local.properties
.DS_Store
/build
**/build/**
/captures
.externalNativeBuild
.cxx
/app/release
/buildSrc/build
**/debug/**
**/release/**
# Java KeyStore certificates
*.jks
@ -33,6 +35,7 @@ keystore.properties
# Secrets
/secrets.properties
/fastlane/play-store-credentials.json
**/google-services.json
/fastlane/report.xml

4
app/.gitignore vendored
Wyświetl plik

@ -1,4 +0,0 @@
/build
/debug
/release
google-services.json

Wyświetl plik

@ -200,8 +200,10 @@ androidComponents {
project.afterEvaluate { logger.lifecycle("Version code is set to: ${android.defaultConfig.versionCode}") }
dependencies {
implementation(projects.navigation)
implementation(projects.network)
implementation(projects.core.model)
implementation(projects.core.navigation)
implementation(projects.core.network)
implementation(projects.core.prefs)
// Bundles
implementation(libs.bundles.markdown)

Wyświetl plik

@ -43,7 +43,6 @@ import com.geeksville.mesh.android.GeeksvilleApplication
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.android.prefs.UiPrefs
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.navigation.DEEP_LINK_BASE_URI
import com.geeksville.mesh.ui.MainScreen
import com.geeksville.mesh.ui.common.theme.AppTheme
import com.geeksville.mesh.ui.common.theme.MODE_DYNAMIC
@ -51,6 +50,7 @@ import com.geeksville.mesh.ui.intro.AppIntroductionScreen
import com.geeksville.mesh.ui.sharing.toSharedContact
import com.geeksville.mesh.util.LanguageUtils
import dagger.hilt.android.AndroidEntryPoint
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import javax.inject.Inject
@AndroidEntryPoint

Wyświetl plik

@ -21,8 +21,8 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.geeksville.mesh.model.DeviceHardware
import com.geeksville.mesh.network.model.NetworkDeviceHardware
import kotlinx.serialization.Serializable
import org.meshtastic.core.network.model.NetworkDeviceHardware
@Serializable
@Entity(tableName = "device_hardware")

Wyświetl plik

@ -21,27 +21,19 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.geeksville.mesh.model.DeviceVersion
import com.geeksville.mesh.network.model.NetworkFirmwareRelease
import kotlinx.serialization.Serializable
import org.meshtastic.core.network.model.NetworkFirmwareRelease
@Serializable
@Entity(tableName = "firmware_release")
data class FirmwareReleaseEntity(
@PrimaryKey
@ColumnInfo(name = "id")
val id: String = "",
@ColumnInfo(name = "page_url")
val pageUrl: String = "",
@ColumnInfo(name = "release_notes")
val releaseNotes: String = "",
@ColumnInfo(name = "title")
val title: String = "",
@ColumnInfo(name = "zip_url")
val zipUrl: String = "",
@ColumnInfo(name = "last_updated")
val lastUpdated: Long = System.currentTimeMillis(),
@ColumnInfo(name = "release_type")
val releaseType: FirmwareReleaseType = FirmwareReleaseType.STABLE,
@PrimaryKey @ColumnInfo(name = "id") val id: String = "",
@ColumnInfo(name = "page_url") val pageUrl: String = "",
@ColumnInfo(name = "release_notes") val releaseNotes: String = "",
@ColumnInfo(name = "title") val title: String = "",
@ColumnInfo(name = "zip_url") val zipUrl: String = "",
@ColumnInfo(name = "last_updated") val lastUpdated: Long = System.currentTimeMillis(),
@ColumnInfo(name = "release_type") val releaseType: FirmwareReleaseType = FirmwareReleaseType.STABLE,
)
fun NetworkFirmwareRelease.asEntity(releaseType: FirmwareReleaseType) = FirmwareReleaseEntity(
@ -74,19 +66,11 @@ data class FirmwareRelease(
val releaseType: FirmwareReleaseType = FirmwareReleaseType.STABLE,
)
fun FirmwareReleaseEntity.asDeviceVersion(): DeviceVersion {
return DeviceVersion(
id.substringBeforeLast(".").replace("v", "")
)
}
fun FirmwareReleaseEntity.asDeviceVersion(): DeviceVersion = DeviceVersion(id.substringBeforeLast(".").replace("v", ""))
fun FirmwareRelease.asDeviceVersion(): DeviceVersion {
return DeviceVersion(
id.substringBeforeLast(".").replace("v", "")
)
}
fun FirmwareRelease.asDeviceVersion(): DeviceVersion = DeviceVersion(id.substringBeforeLast(".").replace("v", ""))
enum class FirmwareReleaseType {
STABLE,
ALPHA
ALPHA,
}

Wyświetl plik

@ -42,7 +42,6 @@ import com.geeksville.mesh.database.MeshLogRepository
import com.geeksville.mesh.database.entity.FirmwareRelease
import com.geeksville.mesh.database.entity.MeshLog
import com.geeksville.mesh.model.map.CustomTileSource
import com.geeksville.mesh.navigation.NodesRoutes
import com.geeksville.mesh.repository.api.DeviceHardwareRepository
import com.geeksville.mesh.repository.api.FirmwareReleaseRepository
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
@ -63,6 +62,7 @@ import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.meshtastic.core.navigation.NodesRoutes
import java.io.BufferedWriter
import java.io.FileNotFoundException
import java.io.FileWriter

Wyświetl plik

@ -28,6 +28,8 @@ import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.settings.radio.components.ChannelConfigScreen
import com.geeksville.mesh.ui.settings.radio.components.LoRaConfigScreen
import com.geeksville.mesh.ui.sharing.ChannelScreen
import org.meshtastic.core.navigation.ChannelsRoutes
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
/** Navigation graph for for the top level ChannelScreen - [ChannelsRoutes.Channels]. */
fun NavGraphBuilder.channelsGraph(navController: NavHostController, uiViewModel: UIViewModel) {

Wyświetl plik

@ -26,6 +26,10 @@ import androidx.navigation.navDeepLink
import androidx.navigation.navigation
import com.geeksville.mesh.ui.connections.ConnectionsScreen
import com.geeksville.mesh.ui.settings.radio.components.LoRaConfigScreen
import org.meshtastic.core.navigation.ConnectionsRoutes
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.navigation.NodesRoutes
import org.meshtastic.core.navigation.SettingsRoutes
/** Navigation graph for for the top level ConnectionsScreen - [ConnectionsRoutes.Connections]. */
fun NavGraphBuilder.connectionsGraph(navController: NavHostController) {

Wyświetl plik

@ -28,6 +28,10 @@ import com.geeksville.mesh.ui.contact.ContactsScreen
import com.geeksville.mesh.ui.message.MessageScreen
import com.geeksville.mesh.ui.message.QuickChatScreen
import com.geeksville.mesh.ui.sharing.ShareScreen
import org.meshtastic.core.navigation.ChannelsRoutes
import org.meshtastic.core.navigation.ContactsRoutes
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.navigation.NodesRoutes
@Suppress("LongMethod")
fun NavGraphBuilder.contactsGraph(navController: NavHostController, uiViewModel: UIViewModel) {

Wyświetl plik

@ -23,6 +23,9 @@ import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.map.MapScreen
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.navigation.MapRoutes
import org.meshtastic.core.navigation.NodesRoutes
fun NavGraphBuilder.mapGraph(navController: NavHostController, uiViewModel: UIViewModel) {
composable<MapRoutes.Map>(deepLinks = listOf(navDeepLink<MapRoutes.Map>(basePath = "$DEEP_LINK_BASE_URI/map"))) {

Wyświetl plik

@ -53,6 +53,11 @@ import com.geeksville.mesh.ui.metrics.TracerouteLogScreen
import com.geeksville.mesh.ui.node.NodeDetailScreen
import com.geeksville.mesh.ui.node.NodeMapScreen
import com.geeksville.mesh.ui.node.NodeScreen
import org.meshtastic.core.navigation.ContactsRoutes
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.navigation.NodeDetailRoutes
import org.meshtastic.core.navigation.NodesRoutes
import org.meshtastic.core.navigation.Route
fun NavGraphBuilder.nodesGraph(navController: NavHostController, uiViewModel: UIViewModel) {
navigation<NodesRoutes.NodesGraph>(startDestination = NodesRoutes.Nodes) {

Wyświetl plik

@ -84,6 +84,10 @@ import com.geeksville.mesh.ui.settings.radio.components.SerialConfigScreen
import com.geeksville.mesh.ui.settings.radio.components.StoreForwardConfigScreen
import com.geeksville.mesh.ui.settings.radio.components.TelemetryConfigScreen
import com.geeksville.mesh.ui.settings.radio.components.UserConfigScreen
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.navigation.NodesRoutes
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
fun getNavRouteFrom(routeName: String): Route? =
ConfigRoute.entries.find { it.name == routeName }?.route ?: ModuleRoute.entries.find { it.name == routeName }?.route

Wyświetl plik

@ -18,15 +18,13 @@
package com.geeksville.mesh.repository.api
import android.app.Application
import com.geeksville.mesh.network.model.NetworkDeviceHardware
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import org.meshtastic.core.network.model.NetworkDeviceHardware
import javax.inject.Inject
class DeviceHardwareJsonDataSource @Inject constructor(
private val application: Application,
) {
class DeviceHardwareJsonDataSource @Inject constructor(private val application: Application) {
@OptIn(ExperimentalSerializationApi::class)
fun loadDeviceHardwareFromJsonAsset(): List<NetworkDeviceHardware> {
val inputStream = application.assets.open("device_hardware.json")

Wyświetl plik

@ -20,30 +20,24 @@ package com.geeksville.mesh.repository.api
import com.geeksville.mesh.database.dao.DeviceHardwareDao
import com.geeksville.mesh.database.entity.DeviceHardwareEntity
import com.geeksville.mesh.database.entity.asEntity
import com.geeksville.mesh.network.model.NetworkDeviceHardware
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.meshtastic.core.network.model.NetworkDeviceHardware
import javax.inject.Inject
class DeviceHardwareLocalDataSource @Inject constructor(
private val deviceHardwareDaoLazy: dagger.Lazy<DeviceHardwareDao>
class DeviceHardwareLocalDataSource
@Inject
constructor(
private val deviceHardwareDaoLazy: dagger.Lazy<DeviceHardwareDao>,
) {
private val deviceHardwareDao by lazy {
deviceHardwareDaoLazy.get()
private val deviceHardwareDao by lazy { deviceHardwareDaoLazy.get() }
suspend fun insertAllDeviceHardware(deviceHardware: List<NetworkDeviceHardware>) = withContext(Dispatchers.IO) {
deviceHardware.forEach { deviceHardware -> deviceHardwareDao.insert(deviceHardware.asEntity()) }
}
suspend fun insertAllDeviceHardware(deviceHardware: List<NetworkDeviceHardware>) =
withContext(Dispatchers.IO) {
deviceHardware.forEach { deviceHardware ->
deviceHardwareDao.insert(deviceHardware.asEntity())
}
}
suspend fun deleteAllDeviceHardware() = withContext(Dispatchers.IO) { deviceHardwareDao.deleteAll() }
suspend fun deleteAllDeviceHardware() = withContext(Dispatchers.IO) {
deviceHardwareDao.deleteAll()
}
suspend fun getByHwModel(hwModel: Int): DeviceHardwareEntity? = withContext(Dispatchers.IO) {
deviceHardwareDao.getByHwModel(hwModel)
}
suspend fun getByHwModel(hwModel: Int): DeviceHardwareEntity? =
withContext(Dispatchers.IO) { deviceHardwareDao.getByHwModel(hwModel) }
}

Wyświetl plik

@ -22,9 +22,9 @@ import com.geeksville.mesh.android.BuildUtils.warn
import com.geeksville.mesh.database.entity.DeviceHardwareEntity
import com.geeksville.mesh.database.entity.asExternalModel
import com.geeksville.mesh.model.DeviceHardware
import com.geeksville.mesh.network.DeviceHardwareRemoteDataSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.meshtastic.core.network.DeviceHardwareRemoteDataSource
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton

Wyświetl plik

@ -18,21 +18,17 @@
package com.geeksville.mesh.repository.api
import android.app.Application
import com.geeksville.mesh.network.model.NetworkFirmwareReleases
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import org.meshtastic.core.network.model.NetworkFirmwareReleases
import javax.inject.Inject
class FirmwareReleaseJsonDataSource @Inject constructor(
private val application: Application,
) {
class FirmwareReleaseJsonDataSource @Inject constructor(private val application: Application) {
@OptIn(ExperimentalSerializationApi::class)
fun loadFirmwareReleaseFromJsonAsset(): NetworkFirmwareReleases {
val inputStream = application.assets.open("firmware_releases.json")
val result = inputStream.use {
Json.decodeFromStream<NetworkFirmwareReleases>(inputStream)
}
val result = inputStream.use { Json.decodeFromStream<NetworkFirmwareReleases>(inputStream) }
return result
}
}

Wyświetl plik

@ -22,44 +22,36 @@ import com.geeksville.mesh.database.entity.FirmwareReleaseEntity
import com.geeksville.mesh.database.entity.FirmwareReleaseType
import com.geeksville.mesh.database.entity.asDeviceVersion
import com.geeksville.mesh.database.entity.asEntity
import com.geeksville.mesh.network.model.NetworkFirmwareRelease
import dagger.Lazy
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.meshtastic.core.network.model.NetworkFirmwareRelease
import javax.inject.Inject
class FirmwareReleaseLocalDataSource @Inject constructor(
private val firmwareReleaseDaoLazy: Lazy<FirmwareReleaseDao>
) {
private val firmwareReleaseDao by lazy {
firmwareReleaseDaoLazy.get()
}
class FirmwareReleaseLocalDataSource @Inject constructor(private val firmwareReleaseDaoLazy: Lazy<FirmwareReleaseDao>) {
private val firmwareReleaseDao by lazy { firmwareReleaseDaoLazy.get() }
suspend fun insertFirmwareReleases(
firmwareReleases: List<NetworkFirmwareRelease>,
releaseType: FirmwareReleaseType
) =
withContext(Dispatchers.IO) {
if (firmwareReleases.isNotEmpty()) {
firmwareReleaseDao.deleteAll()
firmwareReleases.forEach { firmwareRelease ->
firmwareReleaseDao.insert(firmwareRelease.asEntity(releaseType))
}
releaseType: FirmwareReleaseType,
) = withContext(Dispatchers.IO) {
if (firmwareReleases.isNotEmpty()) {
firmwareReleaseDao.deleteAll()
firmwareReleases.forEach { firmwareRelease ->
firmwareReleaseDao.insert(firmwareRelease.asEntity(releaseType))
}
}
suspend fun deleteAllFirmwareReleases() = withContext(Dispatchers.IO) {
firmwareReleaseDao.deleteAll()
}
suspend fun deleteAllFirmwareReleases() = withContext(Dispatchers.IO) { firmwareReleaseDao.deleteAll() }
suspend fun getLatestRelease(releaseType: FirmwareReleaseType): FirmwareReleaseEntity? =
withContext(Dispatchers.IO) {
val releases = firmwareReleaseDao.getReleasesByType(releaseType)
if (releases.isEmpty()) {
return@withContext null
} else {
val latestRelease =
releases.maxBy { it.asDeviceVersion() }
val latestRelease = releases.maxBy { it.asDeviceVersion() }
return@withContext latestRelease
}
}

Wyświetl plik

@ -23,9 +23,9 @@ import com.geeksville.mesh.database.entity.FirmwareRelease
import com.geeksville.mesh.database.entity.FirmwareReleaseEntity
import com.geeksville.mesh.database.entity.FirmwareReleaseType
import com.geeksville.mesh.database.entity.asExternalModel
import com.geeksville.mesh.network.FirmwareReleaseRemoteDataSource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import org.meshtastic.core.network.FirmwareReleaseRemoteDataSource
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton

Wyświetl plik

@ -39,9 +39,9 @@ import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.R
import com.geeksville.mesh.TelemetryProtos.LocalStats
import com.geeksville.mesh.database.entity.NodeEntity
import com.geeksville.mesh.navigation.DEEP_LINK_BASE_URI
import com.geeksville.mesh.service.ReplyReceiver.Companion.KEY_TEXT_REPLY
import com.geeksville.mesh.util.formatUptime
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
/**
* Manages the creation and display of all app notifications.

Wyświetl plik

@ -84,13 +84,6 @@ import com.geeksville.mesh.model.BTScanModel
import com.geeksville.mesh.model.DeviceVersion
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.navigation.ConnectionsRoutes
import com.geeksville.mesh.navigation.ContactsRoutes
import com.geeksville.mesh.navigation.MapRoutes
import com.geeksville.mesh.navigation.NodeDetailRoutes
import com.geeksville.mesh.navigation.NodesRoutes
import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.navigation.SettingsRoutes
import com.geeksville.mesh.navigation.channelsGraph
import com.geeksville.mesh.navigation.connectionsGraph
import com.geeksville.mesh.navigation.contactsGraph
@ -120,6 +113,13 @@ import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.meshtastic.core.navigation.ConnectionsRoutes
import org.meshtastic.core.navigation.ContactsRoutes
import org.meshtastic.core.navigation.MapRoutes
import org.meshtastic.core.navigation.NodeDetailRoutes
import org.meshtastic.core.navigation.NodesRoutes
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
import kotlin.reflect.KClass
enum class TopLevelDestination(@StringRes val label: Int, val icon: ImageVector, val route: Route) {

Wyświetl plik

@ -49,9 +49,6 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import com.geeksville.mesh.R
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.navigation.ContactsRoutes
import com.geeksville.mesh.navigation.NodesRoutes
import com.geeksville.mesh.navigation.SettingsRoutes
import com.geeksville.mesh.navigation.isConfigRoute
import com.geeksville.mesh.navigation.isNodeDetailRoute
import com.geeksville.mesh.ui.TopLevelDestination.Companion.isTopLevel
@ -59,6 +56,9 @@ import com.geeksville.mesh.ui.common.theme.AppTheme
import com.geeksville.mesh.ui.debug.DebugMenuActions
import com.geeksville.mesh.ui.node.components.NodeChip
import com.geeksville.mesh.ui.node.components.NodeMenuAction
import org.meshtastic.core.navigation.ContactsRoutes
import org.meshtastic.core.navigation.NodesRoutes
import org.meshtastic.core.navigation.SettingsRoutes
@Suppress("CyclomaticComplexMethod")
@Composable

Wyświetl plik

@ -65,8 +65,6 @@ import com.geeksville.mesh.model.BTScanModel
import com.geeksville.mesh.model.DeviceListEntry
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.navigation.ConfigRoute
import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.navigation.SettingsRoutes
import com.geeksville.mesh.navigation.getNavRouteFrom
import com.geeksville.mesh.service.ConnectionState
import com.geeksville.mesh.ui.common.components.MainAppBar
@ -83,6 +81,8 @@ import com.geeksville.mesh.ui.settings.radio.components.PacketResponseStateDialo
import com.geeksville.mesh.ui.sharing.SharedContactDialog
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import kotlinx.coroutines.delay
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
fun String?.isIPAddress(): Boolean = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
@Suppress("DEPRECATION")

Wyświetl plik

@ -46,7 +46,6 @@ import androidx.compose.material.icons.filled.BlurOn
import androidx.compose.material.icons.filled.Bolt
import androidx.compose.material.icons.filled.ChargingStation
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.ForkLeft
import androidx.compose.material.icons.filled.Height
@ -135,9 +134,6 @@ import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.model.isUnmessageableRole
import com.geeksville.mesh.navigation.NodeDetailRoutes
import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.navigation.SettingsRoutes
import com.geeksville.mesh.service.ServiceAction
import com.geeksville.mesh.ui.common.components.MainAppBar
import com.geeksville.mesh.ui.common.components.TitledCard
@ -163,6 +159,9 @@ import com.geeksville.mesh.util.toDistanceString
import com.geeksville.mesh.util.toSmallDistanceString
import com.geeksville.mesh.util.toSpeedString
import com.mikepenz.markdown.m3.Markdown
import org.meshtastic.core.navigation.NodeDetailRoutes
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
private data class VectorMetricInfo(
@StringRes val label: Int,

Wyświetl plik

@ -55,7 +55,6 @@ import com.geeksville.mesh.BuildConfig
import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
import com.geeksville.mesh.R
import com.geeksville.mesh.android.gpsDisabled
import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.navigation.getNavRouteFrom
import com.geeksville.mesh.ui.common.components.MainAppBar
import com.geeksville.mesh.ui.common.components.MultipleChoiceAlertDialog
@ -73,6 +72,7 @@ import com.geeksville.mesh.util.LanguageUtils
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import kotlinx.coroutines.delay
import org.meshtastic.core.navigation.Route
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

Wyświetl plik

@ -45,13 +45,13 @@ import androidx.compose.ui.unit.dp
import com.geeksville.mesh.R
import com.geeksville.mesh.navigation.ConfigRoute
import com.geeksville.mesh.navigation.ModuleRoute
import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.navigation.SettingsRoutes
import com.geeksville.mesh.ui.common.components.TitledCard
import com.geeksville.mesh.ui.common.theme.AppTheme
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
import com.geeksville.mesh.ui.settings.components.SettingsItem
import com.geeksville.mesh.ui.settings.radio.components.WarningDialog
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable

Wyświetl plik

@ -58,7 +58,6 @@ import com.geeksville.mesh.model.toChannelSet
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.navigation.ConfigRoute
import com.geeksville.mesh.navigation.ModuleRoute
import com.geeksville.mesh.navigation.SettingsRoutes
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
import com.geeksville.mesh.repository.location.LocationRepository
import com.geeksville.mesh.service.ConnectionState
@ -78,6 +77,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
import org.meshtastic.core.navigation.SettingsRoutes
import java.io.FileOutputStream
import javax.inject.Inject

Wyświetl plik

@ -102,7 +102,6 @@ import com.geeksville.mesh.model.getChannelUrl
import com.geeksville.mesh.model.qrCode
import com.geeksville.mesh.model.toChannelSet
import com.geeksville.mesh.navigation.ConfigRoute
import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.navigation.getNavRouteFrom
import com.geeksville.mesh.service.ConnectionState
import com.geeksville.mesh.ui.common.components.AdaptiveTwoPane
@ -116,6 +115,7 @@ import com.google.accompanist.permissions.rememberPermissionState
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions
import kotlinx.coroutines.launch
import org.meshtastic.core.navigation.Route
/**
* Composable screen for managing and sharing Meshtastic channels. Allows users to view, edit, and share channel

Wyświetl plik

@ -31,13 +31,13 @@ internal fun Project.configureSpotless(extension: SpotlessExtension) = extension
target("src/*/kotlin/**/*.kt", "src/*/java/**/*.kt")
targetExclude("**/build/**/*.kt")
ktfmt().kotlinlangStyle().configure { it.setMaxWidth(120) }
ktlint("1.7.1").setEditorConfigPath("../config/spotless/.editorconfig")
ktlint("1.7.1").setEditorConfigPath(rootProject.file("config/spotless/.editorconfig").path)
licenseHeaderFile(rootProject.file("config/spotless/copyright.kt"))
}
kotlinGradle {
target("**/*.gradle.kts")
ktfmt().kotlinlangStyle().configure { it.setMaxWidth(120) }
ktlint("1.7.1").setEditorConfigPath("../config/spotless/.editorconfig")
ktlint("1.7.1").setEditorConfigPath(rootProject.file("config/spotless/.editorconfig").path)
licenseHeaderFile(
rootProject.file("config/spotless/copyright.kts"),
"(^(?![\\/ ]\\*).*$)"

Wyświetl plik

@ -76,7 +76,11 @@ kover {
}
dependencies {
kover(project(":app"))
kover(project(":network"))
kover(project(":mesh_service_example"))
kover(projects.app)
kover(projects.meshServiceExample)
kover(projects.core.model)
kover(projects.core.navigation)
kover(projects.core.network)
kover(projects.core.prefs)
}

Wyświetl plik

@ -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 {}

Wyświetl plik

@ -18,8 +18,9 @@
plugins {
alias(libs.plugins.meshtastic.android.library)
alias(libs.plugins.meshtastic.kotlinx.serialization)
alias(libs.plugins.kover)
}
android { namespace = "com.geeksville.mesh.navigation" }
android { namespace = "org.meshtastic.core.navigation" }
dependencies {}

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.navigation
package org.meshtastic.core.navigation
import kotlinx.serialization.Serializable

Wyświetl plik

@ -27,7 +27,7 @@ plugins {
android {
buildFeatures { buildConfig = true }
namespace = "com.geeksville.mesh.network"
namespace = "org.meshtastic.core.network"
}
dependencies {

Wyświetl plik

@ -15,18 +15,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.network.di
package org.meshtastic.core.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 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)

Wyświetl plik

@ -15,13 +15,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.network.di
package org.meshtastic.core.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
@ -34,6 +31,9 @@ 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)

Wyświetl plik

@ -15,12 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.network
package org.meshtastic.core.network
import com.geeksville.mesh.network.model.NetworkDeviceHardware
import com.geeksville.mesh.network.service.ApiService
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) {

Wyświetl plik

@ -15,12 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.network
package org.meshtastic.core.network
import com.geeksville.mesh.network.model.NetworkFirmwareReleases
import com.geeksville.mesh.network.service.ApiService
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) {

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.network.di
package org.meshtastic.core.network.di
import android.content.Context
import coil3.ImageLoader
@ -26,13 +26,13 @@ import coil3.request.crossfade
import coil3.svg.SvgDecoder
import coil3.util.DebugLogger
import coil3.util.Logger
import com.geeksville.mesh.network.BuildConfig
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

Wyświetl plik

@ -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,
)

Wyświetl plik

@ -15,37 +15,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.network.model
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 = ""
@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()
@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()
@SerialName("pullRequests") val pullRequests: List<NetworkFirmwareRelease> = listOf(),
@SerialName("releases") val releases: Releases = Releases(),
)

Wyświetl plik

@ -15,11 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.network.service
package org.meshtastic.core.network.service
import com.geeksville.mesh.network.model.NetworkDeviceHardware
import com.geeksville.mesh.network.model.NetworkFirmwareReleases
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")

Wyświetl plik

@ -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 {}

Wyświetl plik

@ -1 +0,0 @@
/build

Wyświetl plik

@ -1 +0,0 @@
/build

1
network/.gitignore vendored
Wyświetl plik

@ -1 +0,0 @@
/build

Wyświetl plik

@ -1,51 +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.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
)

Wyświetl plik

@ -17,7 +17,7 @@ import org.gradle.kotlin.dsl.maven
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
include(":app", ":mesh_service_example", ":navigation", ":network")
include(":app", ":core:model", ":core:navigation", ":core:network", ":core:prefs", ":mesh_service_example")
rootProject.name = "MeshtasticAndroid"
// https://docs.gradle.org/current/userguide/declaring_dependencies.html#sec:type-safe-project-accessors