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

36
.github/workflows/publish-packages.yml vendored Normal file
View 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 }}

View file

@ -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.

View file

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

View file

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

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

View 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.")
}

View file

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

View file

@ -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() {

View file

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

View file

@ -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") }