Refactor: Create core:api module and set up publishing (#4362)

This commit is contained in:
James Rich 2026-01-29 13:45:00 -06:00 committed by GitHub
parent 4e7de3b73c
commit 15760da074
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 381 additions and 55 deletions

72
core/api/README.md Normal file
View file

@ -0,0 +1,72 @@
# Meshtastic Android API
This module contains the stable AIDL interface and dependencies required to integrate with the Meshtastic Android app.
## Integration
To communicate with the Meshtastic Android service from your own application, we recommend using **JitPack**.
Add the JitPack repository to your root `build.gradle.kts` (or `settings.gradle.kts`):
```kotlin
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
```
Add the dependencies to your module's `build.gradle.kts`:
```kotlin
dependencies {
// Replace 'v2.7.12' with the specific version you need
val meshtasticVersion = "v2.7.12"
// The core AIDL interface
implementation("com.github.meshtastic.Meshtastic-Android:core-api:$meshtasticVersion")
// Data models (DataPacket, MeshUser, NodeInfo, etc.)
implementation("com.github.meshtastic.Meshtastic-Android:core-model:$meshtasticVersion")
// Protobuf definitions (Portnums, Telemetry, etc.)
implementation("com.github.meshtastic.Meshtastic-Android:core-proto:$meshtasticVersion")
}
```
## Usage
1. **Bind to the Service:**
Use the `IMeshService` interface to bind to the Meshtastic service.
```kotlin
val intent = Intent("com.geeksville.mesh.Service")
intent.setClassName("com.geeksville.mesh", "com.geeksville.mesh.service.MeshService")
bindService(intent, serviceConnection, BIND_AUTO_CREATE)
```
2. **Interact with the API:**
Once bound, cast the `IBinder` to `IMeshService`:
```kotlin
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val meshService = IMeshService.Stub.asInterface(service)
// Example: Send a text message
val packet = DataPacket(
to = DataPacket.ID_BROADCAST,
bytes = "Hello Meshtastic!".toByteArray(),
dataType = Portnums.PortNum.TEXT_MESSAGE_APP_VALUE,
// ... other fields
)
meshService.send(packet)
}
```
## Modules
* **`core:api`**: Contains `IMeshService.aidl`.
* **`core:model`**: Contains Parcelable data classes like `DataPacket`, `MeshUser`, `NodeInfo`.
* **`core:proto`**: Contains the generated Protobuf code from `meshtastic/protobufs`.

41
core/api/build.gradle.kts Normal file
View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2026 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)
`maven-publish`
}
apply(from = rootProject.file("gradle/publishing.gradle.kts"))
afterEvaluate {
publishing {
publications {
create<MavenPublication>("release") {
from(components["googleRelease"])
artifactId = "core-api"
}
}
}
}
configure<com.android.build.api.dsl.LibraryExtension> {
namespace = "org.meshtastic.core.api"
buildFeatures { aidl = true }
publishing { singleVariant("googleRelease") { withSourcesJar() } }
}
dependencies { api(projects.core.model) }

View file

@ -0,0 +1 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />

View file

@ -60,3 +60,22 @@ classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000;
</details>
<!--endregion-->
## Meshtastic Core Models
This module contains the Parcelable data classes used by the Meshtastic Android app and its API. These models are designed to be shared between the service and client applications via AIDL.
### Key Classes
* **`DataPacket`**: Represents a mesh packet (text, telemetry, etc.).
* **`MeshUser`**: Represents a user/node on the mesh.
* **`NodeInfo`**: Contains detailed information about a node (position, SNR, battery, etc.).
* **`Position`**: GPS location data.
### Usage
This module is typically used as a dependency of `core:api` but can be used independently if you need to work with Meshtastic data structures.
```kotlin
implementation("com.github.meshtastic.Meshtastic-Android:core-model:v2.7.12")
```

View file

@ -20,6 +20,20 @@ plugins {
alias(libs.plugins.meshtastic.android.library)
alias(libs.plugins.meshtastic.kotlinx.serialization)
alias(libs.plugins.kotlin.parcelize)
`maven-publish`
}
apply(from = rootProject.file("gradle/publishing.gradle.kts"))
afterEvaluate {
publishing {
publications {
create<MavenPublication>("release") {
from(components["googleRelease"])
artifactId = "core-model"
}
}
}
}
configure<LibraryExtension> {
@ -28,12 +42,11 @@ configure<LibraryExtension> {
aidl = true
}
namespace = "org.meshtastic.core.model"
publishing { singleVariant("googleRelease") { withSourcesJar() } }
}
dependencies {
implementation(projects.core.common)
implementation(projects.core.proto)
implementation(projects.core.strings)
implementation(libs.androidx.annotation)
implementation(libs.kotlinx.serialization.json)

View file

@ -17,8 +17,8 @@
package org.meshtastic.core.model
import com.google.protobuf.ByteString
import org.meshtastic.core.common.byteArrayOfInts
import org.meshtastic.core.common.xorHash
import org.meshtastic.core.model.util.byteArrayOfInts
import org.meshtastic.core.model.util.xorHash
import org.meshtastic.proto.ChannelProtos
import org.meshtastic.proto.ConfigKt.loRaConfig
import org.meshtastic.proto.ConfigProtos

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 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
@ -14,23 +14,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@file:Suppress("MagicNumber")
package org.meshtastic.core.model
import org.jetbrains.compose.resources.StringResource
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.label_long_fast
import org.meshtastic.core.strings.label_long_moderate
import org.meshtastic.core.strings.label_long_slow
import org.meshtastic.core.strings.label_long_turbo
import org.meshtastic.core.strings.label_medium_fast
import org.meshtastic.core.strings.label_medium_slow
import org.meshtastic.core.strings.label_short_fast
import org.meshtastic.core.strings.label_short_slow
import org.meshtastic.core.strings.label_short_turbo
import org.meshtastic.core.strings.label_very_long_slow
import org.meshtastic.proto.ConfigProtos.Config.LoRaConfig
import org.meshtastic.proto.ConfigProtos.Config.LoRaConfig.ModemPreset
import org.meshtastic.proto.ConfigProtos.Config.LoRaConfig.RegionCode
@ -308,18 +295,18 @@ enum class RegionInfo(
}
}
enum class ChannelOption(val modemPreset: ModemPreset, val labelRes: StringResource, val bandwidth: Float) {
enum class ChannelOption(val modemPreset: ModemPreset, val bandwidth: Float) {
// Grouped by range and speed for better readability
VERY_LONG_SLOW(ModemPreset.VERY_LONG_SLOW, Res.string.label_very_long_slow, 0.0625f),
LONG_TURBO(ModemPreset.LONG_TURBO, Res.string.label_long_turbo, 0.500f),
LONG_FAST(ModemPreset.LONG_FAST, Res.string.label_long_fast, 0.250f),
LONG_MODERATE(ModemPreset.LONG_MODERATE, Res.string.label_long_moderate, 0.125f),
LONG_SLOW(ModemPreset.LONG_SLOW, Res.string.label_long_slow, 0.125f),
MEDIUM_FAST(ModemPreset.MEDIUM_FAST, Res.string.label_medium_fast, 0.250f),
MEDIUM_SLOW(ModemPreset.MEDIUM_SLOW, Res.string.label_medium_slow, 0.250f),
SHORT_FAST(ModemPreset.SHORT_FAST, Res.string.label_short_fast, 0.250f),
SHORT_SLOW(ModemPreset.SHORT_SLOW, Res.string.label_short_slow, 0.250f),
SHORT_TURBO(ModemPreset.SHORT_TURBO, Res.string.label_short_turbo, 0.500f),
VERY_LONG_SLOW(ModemPreset.VERY_LONG_SLOW, 0.0625f),
LONG_TURBO(ModemPreset.LONG_TURBO, 0.500f),
LONG_FAST(ModemPreset.LONG_FAST, 0.250f),
LONG_MODERATE(ModemPreset.LONG_MODERATE, 0.125f),
LONG_SLOW(ModemPreset.LONG_SLOW, 0.125f),
MEDIUM_FAST(ModemPreset.MEDIUM_FAST, 0.250f),
MEDIUM_SLOW(ModemPreset.MEDIUM_SLOW, 0.250f),
SHORT_FAST(ModemPreset.SHORT_FAST, 0.250f),
SHORT_SLOW(ModemPreset.SHORT_SLOW, 0.250f),
SHORT_TURBO(ModemPreset.SHORT_TURBO, 0.500f),
;
companion object {

View file

@ -16,10 +16,6 @@
*/
package org.meshtastic.core.model
import org.jetbrains.compose.resources.StringResource
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.traceroute_endpoint_missing
import org.meshtastic.core.strings.traceroute_map_no_data
import org.meshtastic.proto.MeshProtos
import org.meshtastic.proto.MeshProtos.RouteDiscovery
import org.meshtastic.proto.Portnums
@ -129,9 +125,3 @@ fun evaluateTracerouteMapAvailability(
val hasAnyMappable = relatedNodeNums.any { positionedNodeNums.contains(it) }
return if (hasAnyMappable) TracerouteMapAvailability.Ok else TracerouteMapAvailability.NoMappableNodes
}
fun TracerouteMapAvailability.toMessageRes(): StringResource? = when (this) {
TracerouteMapAvailability.Ok -> null
TracerouteMapAvailability.MissingEndpoints -> Res.string.traceroute_endpoint_missing
TracerouteMapAvailability.NoMappableNodes -> Res.string.traceroute_map_no_data
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2025-2026 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.model.util
/** Utility function to make it easy to declare byte arrays */
fun byteArrayOfInts(vararg ints: Int) = ByteArray(ints.size) { pos -> ints[pos].toByte() }
fun xorHash(b: ByteArray) = b.fold(0) { acc, x -> acc xor (x.toInt() and BYTE_MASK) }
private const val BYTE_MASK = 0xff

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 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
@ -36,9 +36,26 @@ plugins {
alias(libs.plugins.meshtastic.android.library)
alias(libs.plugins.meshtastic.android.library.compose)
alias(libs.plugins.protobuf)
`maven-publish`
}
configure<LibraryExtension> { namespace = "org.meshtastic.core.proto" }
apply(from = rootProject.file("gradle/publishing.gradle.kts"))
afterEvaluate {
publishing {
publications {
create<MavenPublication>("release") {
from(components["googleRelease"])
artifactId = "core-proto"
}
}
}
}
configure<LibraryExtension> {
namespace = "org.meshtastic.core.proto"
publishing { singleVariant("googleRelease") { withSourcesJar() } }
}
// per protobuf-gradle-plugin docs, this is recommended for android
protobuf {

View file

@ -26,6 +26,7 @@ configure<LibraryExtension> {
}
dependencies {
api(projects.core.api)
implementation(projects.core.database)
implementation(projects.core.model)
implementation(projects.core.prefs)

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2025-2026 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.ui.util
import org.jetbrains.compose.resources.StringResource
import org.meshtastic.core.model.ChannelOption
import org.meshtastic.core.model.TracerouteMapAvailability
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.label_long_fast
import org.meshtastic.core.strings.label_long_moderate
import org.meshtastic.core.strings.label_long_slow
import org.meshtastic.core.strings.label_long_turbo
import org.meshtastic.core.strings.label_medium_fast
import org.meshtastic.core.strings.label_medium_slow
import org.meshtastic.core.strings.label_short_fast
import org.meshtastic.core.strings.label_short_slow
import org.meshtastic.core.strings.label_short_turbo
import org.meshtastic.core.strings.label_very_long_slow
import org.meshtastic.core.strings.traceroute_endpoint_missing
import org.meshtastic.core.strings.traceroute_map_no_data
val ChannelOption.labelRes: StringResource
get() =
when (this) {
ChannelOption.VERY_LONG_SLOW -> Res.string.label_very_long_slow
ChannelOption.LONG_TURBO -> Res.string.label_long_turbo
ChannelOption.LONG_FAST -> Res.string.label_long_fast
ChannelOption.LONG_MODERATE -> Res.string.label_long_moderate
ChannelOption.LONG_SLOW -> Res.string.label_long_slow
ChannelOption.MEDIUM_FAST -> Res.string.label_medium_fast
ChannelOption.MEDIUM_SLOW -> Res.string.label_medium_slow
ChannelOption.SHORT_FAST -> Res.string.label_short_fast
ChannelOption.SHORT_SLOW -> Res.string.label_short_slow
ChannelOption.SHORT_TURBO -> Res.string.label_short_turbo
}
fun TracerouteMapAvailability.toMessageRes(): StringResource? = when (this) {
TracerouteMapAvailability.Ok -> null
TracerouteMapAvailability.MissingEndpoints -> Res.string.traceroute_endpoint_missing
TracerouteMapAvailability.NoMappableNodes -> Res.string.traceroute_map_no_data
}