mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Refactor: Create core:api module and set up publishing (#4362)
This commit is contained in:
parent
4e7de3b73c
commit
15760da074
23 changed files with 381 additions and 55 deletions
36
.github/workflows/publish-packages.yml
vendored
Normal file
36
.github/workflows/publish-packages.yml
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
name: Publish Packages
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'jetbrains'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
with:
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
|
||||
- name: Publish to GitHub Packages
|
||||
run: ./gradlew :core:api:publish :core:model:publish :core:proto:publish
|
||||
env:
|
||||
GITHUB_ACTOR: ${{ github.actor }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
@ -60,6 +60,12 @@ You can generate the documentation locally to preview your changes.
|
|||
|
||||
You can help translate the app into your native language using [Crowdin](https://crowdin.meshtastic.org/android).
|
||||
|
||||
## API & Integration
|
||||
|
||||
Developers can integrate with the Meshtastic Android app using our published API library via **JitPack**. This allows third-party applications (like the ATAK plugin) to communicate with the mesh service via AIDL.
|
||||
|
||||
For detailed integration instructions, see [core/api/README.md](core/api/README.md).
|
||||
|
||||
## Building the Android App
|
||||
> [!WARNING]
|
||||
> Debug and release builds can be installed concurrently. This is solely to enable smoother development, and you should avoid running both apps simultaneously. To ensure proper function, force quit the app not in use.
|
||||
|
|
|
|||
|
|
@ -105,7 +105,6 @@ import org.jetbrains.compose.resources.StringResource
|
|||
import org.jetbrains.compose.resources.getString
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.model.DeviceVersion
|
||||
import org.meshtastic.core.model.toMessageRes
|
||||
import org.meshtastic.core.navigation.ConnectionsRoutes
|
||||
import org.meshtastic.core.navigation.ContactsRoutes
|
||||
import org.meshtastic.core.navigation.MapRoutes
|
||||
|
|
@ -149,6 +148,7 @@ import org.meshtastic.core.ui.qr.ScannedQrCodeDialog
|
|||
import org.meshtastic.core.ui.share.SharedContactDialog
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusBlue
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
|
||||
import org.meshtastic.core.ui.util.toMessageRes
|
||||
import org.meshtastic.feature.node.metrics.annotateTraceroute
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
|
||||
|
|
|
|||
72
core/api/README.md
Normal file
72
core/api/README.md
Normal 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
41
core/api/build.gradle.kts
Normal 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) }
|
||||
1
core/api/src/main/AndroidManifest.xml
Normal file
1
core/api/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
|
||||
|
|
@ -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")
|
||||
```
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ configure<LibraryExtension> {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.core.api)
|
||||
implementation(projects.core.database)
|
||||
implementation(projects.core.model)
|
||||
implementation(projects.core.prefs)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -59,7 +59,6 @@ import org.jetbrains.compose.resources.stringResource
|
|||
import org.meshtastic.core.model.evaluateTracerouteMapAvailability
|
||||
import org.meshtastic.core.model.fullRouteDiscovery
|
||||
import org.meshtastic.core.model.getTracerouteResponse
|
||||
import org.meshtastic.core.model.toMessageRes
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.close
|
||||
import org.meshtastic.core.strings.routing_error_no_response
|
||||
|
|
@ -86,6 +85,7 @@ import org.meshtastic.core.ui.theme.AppTheme
|
|||
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusOrange
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusYellow
|
||||
import org.meshtastic.core.ui.util.toMessageRes
|
||||
import org.meshtastic.feature.map.model.TracerouteOverlay
|
||||
import org.meshtastic.feature.node.component.CooldownIconButton
|
||||
import org.meshtastic.feature.node.detail.NodeRequestEffect
|
||||
|
|
|
|||
|
|
@ -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,7 +14,6 @@
|
|||
* 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.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
|
|
@ -61,6 +60,7 @@ import org.meshtastic.core.ui.component.EditTextPreference
|
|||
import org.meshtastic.core.ui.component.SignedIntegerEditTextPreference
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.core.ui.util.labelRes
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.feature.settings.util.hopLimits
|
||||
import org.meshtastic.proto.config
|
||||
|
|
|
|||
34
gradle/publishing.gradle.kts
Normal file
34
gradle/publishing.gradle.kts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import java.util.Properties
|
||||
import java.io.FileInputStream
|
||||
|
||||
val configProperties = Properties()
|
||||
val configFile = rootProject.file("config.properties")
|
||||
if (configFile.exists()) {
|
||||
FileInputStream(configFile).use { configProperties.load(it) }
|
||||
}
|
||||
|
||||
val versionBase = configProperties.getProperty("VERSION_NAME_BASE") ?: "0.0.0-SNAPSHOT"
|
||||
val appVersion = System.getenv("VERSION_NAME") ?: versionBase
|
||||
|
||||
project.version = appVersion
|
||||
project.group = "org.meshtastic"
|
||||
|
||||
val GITHUB_ACTOR = System.getenv("GITHUB_ACTOR")
|
||||
val GITHUB_TOKEN = System.getenv("GITHUB_TOKEN")
|
||||
|
||||
if (!GITHUB_ACTOR.isNullOrEmpty() && !GITHUB_TOKEN.isNullOrEmpty()) {
|
||||
configure<PublishingExtension> {
|
||||
repositories {
|
||||
maven {
|
||||
name = "GitHubPackages"
|
||||
url = uri("https://maven.pkg.github.com/meshtastic/Meshtastic-Android")
|
||||
credentials {
|
||||
username = GITHUB_ACTOR
|
||||
password = GITHUB_TOKEN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println("Skipping GitHub Packages repository configuration: GITHUB_ACTOR or GITHUB_TOKEN not set.")
|
||||
}
|
||||
|
|
@ -37,10 +37,9 @@ configure<ApplicationExtension> {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.core.api)
|
||||
implementation(projects.core.model)
|
||||
implementation(projects.core.proto)
|
||||
implementation(projects.core.service)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ import androidx.activity.ComponentActivity
|
|||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import org.meshtastic.core.service.IMeshService
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
private const val TAG: String = "MeshServiceExample"
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ class MainActivity : ComponentActivity() {
|
|||
registerReceiver(meshtasticReceiver, intentFilter)
|
||||
}
|
||||
|
||||
setContent { AppTheme { MainScreen(viewModel) } }
|
||||
setContent { MaterialTheme { MainScreen(viewModel) } }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
|
|
|||
|
|
@ -75,14 +75,43 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.model.NodeInfo
|
||||
import org.meshtastic.core.ui.component.ListItem
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
|
||||
|
||||
@Composable
|
||||
fun ListItem(
|
||||
text: String,
|
||||
supportingText: String? = null,
|
||||
leadingIcon: ImageVector? = null,
|
||||
trailingIcon: ImageVector? = null,
|
||||
) {
|
||||
androidx.compose.material3.ListItem(
|
||||
headlineContent = { Text(text) },
|
||||
supportingContent = supportingText?.let { { Text(it) } },
|
||||
leadingContent = leadingIcon?.let { { Icon(it, contentDescription = null) } },
|
||||
trailingContent = trailingIcon?.let { { Icon(it, contentDescription = null) } },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TitledCard(title: String, content: @Composable () -> Unit) {
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
)
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -132,7 +161,7 @@ private fun TopBarTitle(isConnected: Boolean, connectionState: String) {
|
|||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
val statusColor =
|
||||
if (isConnected) {
|
||||
MaterialTheme.colorScheme.StatusGreen
|
||||
Color.Green
|
||||
} else {
|
||||
MaterialTheme.colorScheme.error
|
||||
}
|
||||
|
|
@ -331,7 +360,7 @@ private fun NodeItemHeader(node: NodeInfo) {
|
|||
.background(MaterialTheme.colorScheme.surface)
|
||||
.padding(2.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.StatusGreen),
|
||||
.background(Color.Green),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -393,7 +422,7 @@ private fun NodeItemActions(isOnline: Boolean, onAction: (String) -> Unit) {
|
|||
Icon(
|
||||
imageVector = Icons.Rounded.Router,
|
||||
contentDescription = "Online",
|
||||
tint = MaterialTheme.colorScheme.StatusGreen.copy(alpha = 0.5f),
|
||||
tint = androidx.compose.ui.graphics.Color.Green.copy(alpha = 0.5f),
|
||||
modifier = Modifier.padding(start = 8.dp).size(20.dp),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
include(
|
||||
":app",
|
||||
":core:analytics",
|
||||
":core:api",
|
||||
":core:common",
|
||||
":core:data",
|
||||
":core:database",
|
||||
|
|
@ -58,6 +59,7 @@ pluginManagement {
|
|||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
mavenLocal()
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue