diff --git a/.github/workflows/publish-packages.yml b/.github/workflows/publish-packages.yml new file mode 100644 index 000000000..3d20ec9e2 --- /dev/null +++ b/.github/workflows/publish-packages.yml @@ -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 }} diff --git a/README.md b/README.md index 033d29cad..59c4824f5 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/app/src/main/java/com/geeksville/mesh/ui/Main.kt b/app/src/main/java/com/geeksville/mesh/ui/Main.kt index f45d749eb..c45ac99f6 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Main.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Main.kt @@ -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 diff --git a/core/api/README.md b/core/api/README.md new file mode 100644 index 000000000..b90733145 --- /dev/null +++ b/core/api/README.md @@ -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`. diff --git a/core/api/build.gradle.kts b/core/api/build.gradle.kts new file mode 100644 index 000000000..65a88a57e --- /dev/null +++ b/core/api/build.gradle.kts @@ -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 . + */ +plugins { + alias(libs.plugins.meshtastic.android.library) + `maven-publish` +} + +apply(from = rootProject.file("gradle/publishing.gradle.kts")) + +afterEvaluate { + publishing { + publications { + create("release") { + from(components["googleRelease"]) + artifactId = "core-api" + } + } + } +} + +configure { + namespace = "org.meshtastic.core.api" + buildFeatures { aidl = true } + publishing { singleVariant("googleRelease") { withSourcesJar() } } +} + +dependencies { api(projects.core.model) } diff --git a/core/api/src/main/AndroidManifest.xml b/core/api/src/main/AndroidManifest.xml new file mode 100644 index 000000000..94cbbcfc3 --- /dev/null +++ b/core/api/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/core/service/src/main/aidl/org/meshtastic/core/service/IMeshService.aidl b/core/api/src/main/aidl/org/meshtastic/core/service/IMeshService.aidl similarity index 100% rename from core/service/src/main/aidl/org/meshtastic/core/service/IMeshService.aidl rename to core/api/src/main/aidl/org/meshtastic/core/service/IMeshService.aidl diff --git a/core/model/README.md b/core/model/README.md index 9edc886dd..f1f1714b5 100644 --- a/core/model/README.md +++ b/core/model/README.md @@ -60,3 +60,22 @@ classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000; + +## 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") +``` \ No newline at end of file diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts index 8c6bc6c63..045132519 100644 --- a/core/model/build.gradle.kts +++ b/core/model/build.gradle.kts @@ -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("release") { + from(components["googleRelease"]) + artifactId = "core-model" + } + } + } } configure { @@ -28,12 +42,11 @@ configure { 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) diff --git a/core/model/src/main/kotlin/org/meshtastic/core/model/Channel.kt b/core/model/src/main/kotlin/org/meshtastic/core/model/Channel.kt index b33368172..4a3c2a7be 100644 --- a/core/model/src/main/kotlin/org/meshtastic/core/model/Channel.kt +++ b/core/model/src/main/kotlin/org/meshtastic/core/model/Channel.kt @@ -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 diff --git a/core/model/src/main/kotlin/org/meshtastic/core/model/ChannelOption.kt b/core/model/src/main/kotlin/org/meshtastic/core/model/ChannelOption.kt index 958977053..0e8712059 100644 --- a/core/model/src/main/kotlin/org/meshtastic/core/model/ChannelOption.kt +++ b/core/model/src/main/kotlin/org/meshtastic/core/model/ChannelOption.kt @@ -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 . */ - @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 { diff --git a/core/model/src/main/kotlin/org/meshtastic/core/model/RouteDiscovery.kt b/core/model/src/main/kotlin/org/meshtastic/core/model/RouteDiscovery.kt index 9d65255af..de47abc4d 100644 --- a/core/model/src/main/kotlin/org/meshtastic/core/model/RouteDiscovery.kt +++ b/core/model/src/main/kotlin/org/meshtastic/core/model/RouteDiscovery.kt @@ -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 -} diff --git a/core/model/src/main/kotlin/org/meshtastic/core/model/util/CommonUtils.kt b/core/model/src/main/kotlin/org/meshtastic/core/model/util/CommonUtils.kt new file mode 100644 index 000000000..0faad412a --- /dev/null +++ b/core/model/src/main/kotlin/org/meshtastic/core/model/util/CommonUtils.kt @@ -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 . + */ +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 diff --git a/core/proto/build.gradle.kts b/core/proto/build.gradle.kts index 217e5c018..1e20691a2 100644 --- a/core/proto/build.gradle.kts +++ b/core/proto/build.gradle.kts @@ -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 { namespace = "org.meshtastic.core.proto" } +apply(from = rootProject.file("gradle/publishing.gradle.kts")) + +afterEvaluate { + publishing { + publications { + create("release") { + from(components["googleRelease"]) + artifactId = "core-proto" + } + } + } +} + +configure { + namespace = "org.meshtastic.core.proto" + publishing { singleVariant("googleRelease") { withSourcesJar() } } +} // per protobuf-gradle-plugin docs, this is recommended for android protobuf { diff --git a/core/service/build.gradle.kts b/core/service/build.gradle.kts index 309deb29d..609028f7d 100644 --- a/core/service/build.gradle.kts +++ b/core/service/build.gradle.kts @@ -26,6 +26,7 @@ configure { } dependencies { + api(projects.core.api) implementation(projects.core.database) implementation(projects.core.model) implementation(projects.core.prefs) diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/ModelExtensions.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/ModelExtensions.kt new file mode 100644 index 000000000..e52108883 --- /dev/null +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/ModelExtensions.kt @@ -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 . + */ +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 +} diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt index 60ca71d99..3847654d5 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt @@ -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 diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/LoRaConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/LoRaConfigItemList.kt index a83dba382..8e2a46754 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/LoRaConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/LoRaConfigItemList.kt @@ -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 . */ - 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 diff --git a/gradle/publishing.gradle.kts b/gradle/publishing.gradle.kts new file mode 100644 index 000000000..adaa9de7a --- /dev/null +++ b/gradle/publishing.gradle.kts @@ -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 { + 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.") +} diff --git a/mesh_service_example/build.gradle.kts b/mesh_service_example/build.gradle.kts index 33d20268b..8b083656a 100644 --- a/mesh_service_example/build.gradle.kts +++ b/mesh_service_example/build.gradle.kts @@ -37,10 +37,9 @@ configure { } 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) diff --git a/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainActivity.kt b/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainActivity.kt index 178b09e96..08766a9c0 100644 --- a/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainActivity.kt +++ b/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainActivity.kt @@ -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() { diff --git a/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainScreen.kt b/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainScreen.kt index 1004c2bce..d12dc11d6 100644 --- a/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainScreen.kt +++ b/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainScreen.kt @@ -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), ) } diff --git a/settings.gradle.kts b/settings.gradle.kts index f30b55b09..c22659eb2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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") }