diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index 209d6e35c..d450711ce 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -70,8 +70,7 @@ jobs:
}
allowed_extra_roots = {'baselineprofile'}
- excluded_roots = {'mesh_service_example'}
- expected_roots = (module_roots | allowed_extra_roots) - excluded_roots
+ expected_roots = module_roots | allowed_extra_roots
filter_paths = {
path.split('/')[0]
diff --git a/.skills/project-overview/SKILL.md b/.skills/project-overview/SKILL.md
index 291cff488..2224fa7ad 100644
--- a/.skills/project-overview/SKILL.md
+++ b/.skills/project-overview/SKILL.md
@@ -39,7 +39,6 @@ Module directory, namespacing conventions, environment setup, and troubleshootin
| `feature/wifi-provision` | KMP WiFi provisioning via BLE (Nymea protocol). Uses `core:ble` Kable abstractions. |
| `feature/firmware` | Fully KMP firmware update system: Unified OTA (BLE + WiFi), native Nordic Secure DFU protocol (pure KMP), USB/UF2 updates, and `FirmwareRetriever` with manifest-based resolution. Desktop is a first-class target. |
| `desktop/` | Compose Desktop application. Thin host shell relying on feature modules for shared UI. Full Koin DI graph, TCP, Serial/USB, and BLE transports. Versioning via `config.properties` + `GitVersionValueSource`. |
-| `mesh_service_example/` | **DEPRECATED.** Legacy sample app; not yet removed. See `core/api/README.md` for the current integration guide. |
## Namespacing
- **Standard:** Use the `org.meshtastic.*` namespace for all code.
diff --git a/.skills/testing-ci/SKILL.md b/.skills/testing-ci/SKILL.md
index 2c20258c1..1c8b7b901 100644
--- a/.skills/testing-ci/SKILL.md
+++ b/.skills/testing-ci/SKILL.md
@@ -48,7 +48,7 @@ CI is defined in `.github/workflows/reusable-check.yml` and structured as four p
2. **`test-shards`** — A 3-shard matrix that runs unit tests in parallel (depends on `lint-check`):
- `shard-core`: `allTests` for all `core:*` KMP modules.
- `shard-feature`: `allTests` for all `feature:*` KMP modules.
- - `shard-app`: Explicit test tasks for pure-Android/JVM modules (`app`, `desktop`, `core:barcode`, `mesh_service_example`).
+ - `shard-app`: Explicit test tasks for pure-Android/JVM modules (`app`, `desktop`, `core:barcode`).
Each shard generates Kover XML coverage and uploads test results + coverage to Codecov with per-shard flags.
Downstream jobs use `fetch-depth: 1` and receive `VERSION_CODE` from lint-check via env var, enabling shallow clones.
3. **`android-check`** — Builds APKs for all flavors (depends on `lint-check`).
diff --git a/codecov.yml b/codecov.yml
index 6e0989227..7f77510ff 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -57,10 +57,6 @@ component_management:
name: Desktop
paths:
- desktop/**
- - component_id: example
- name: Example
- paths:
- - mesh_service_example/**
ignore:
- "**/build/**"
diff --git a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/ServiceBroadcasts.kt b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/ServiceBroadcasts.kt
index 57408cff1..22bacf43a 100644
--- a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/ServiceBroadcasts.kt
+++ b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/ServiceBroadcasts.kt
@@ -133,7 +133,7 @@ class ServiceBroadcasts(private val context: Context, private val serviceReposit
explicitBroadcast(Intent(ACTION_MESH_DISCONNECTED))
}
- // Restore legacy action for other consumers (e.g. mesh_service_example)
+ // Restore legacy action for other consumers (e.g. ATAK plugins)
val legacyIntent =
Intent(ACTION_CONNECTION_CHANGED).apply {
putExtra(EXTRA_CONNECTED, stateStr)
diff --git a/docs/BUILD_LOGIC_CONVENTIONS_GUIDE.md b/docs/BUILD_LOGIC_CONVENTIONS_GUIDE.md
index 5898f7f94..d3dd5ad93 100644
--- a/docs/BUILD_LOGIC_CONVENTIONS_GUIDE.md
+++ b/docs/BUILD_LOGIC_CONVENTIONS_GUIDE.md
@@ -121,8 +121,8 @@ kotlin {
```
**What the plugin provides automatically:**
-- `commonMain`: `compose-multiplatform-material3`, `compose-multiplatform-materialIconsExtended`, `jetbrains-lifecycle-viewmodel-compose`, `koin-compose-viewmodel`, `kermit`
-- `androidMain`: `androidx-compose-bom` (platform), `accompanist-permissions`, `androidx-activity-compose`, `androidx-compose-material3`, `androidx-compose-material-iconsExtended`, `androidx-compose-ui-text`, `androidx-compose-ui-tooling-preview`
+- `commonMain`: `compose-multiplatform-material3`, `jetbrains-lifecycle-viewmodel-compose`, `jetbrains-lifecycle-runtime-compose`, `koin-compose-viewmodel`, `kermit`
+- `androidMain`: `androidx-compose-bom` (platform), `accompanist-permissions`, `androidx-activity-compose`, `androidx-compose-material3`, `androidx-compose-ui-text`, `androidx-compose-ui-tooling-preview`
- `commonTest`: `core:testing`
**Why:** Eliminates ~15 duplicate dependency declarations per feature module (modelled after Now in Android's `AndroidFeatureImplConventionPlugin`).
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index c3b4c24ca..d1051dc2a 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -122,7 +122,6 @@ androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version
androidx-work-testing = { module = "androidx.work:work-testing", version = "2.11.2" }
# AndroidX Compose (explicit versions — BOM removed; CMP is the sole version authority)
-androidx-compose-material-iconsExtended = { module = "androidx.compose.material:material-icons-extended", version.ref = "androidx-compose-material" } # Only used by deprecated mesh_service_example — remove when that module is deleted
androidx-compose-runtime-tracing = { module = "androidx.compose.runtime:runtime-tracing", version.ref = "compose-multiplatform" }
androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "compose-multiplatform" } # Required by Robolectric Compose tests (registers ComponentActivity)
diff --git a/mesh_service_example/README.md b/mesh_service_example/README.md
deleted file mode 100644
index 3804db328..000000000
--- a/mesh_service_example/README.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# mesh_service_example
-
-> **DEPRECATED — scheduled for removal in a future release.**
->
-> This module is no longer maintained and will be deleted once the new public API documentation is
-> available. Do not add new code here. Do not use it as a template for new integrations.
->
-> For integrating with the Meshtastic service from your own app, refer to the `:core:api` module
-> README at [`core/api/README.md`](../core/api/README.md).
-
-## What this was
-
-`mesh_service_example` was a sample Android application demonstrating how to bind to the
-`IMeshService` AIDL interface and exchange data with the Meshtastic radio service. It is kept in
-the repository only to avoid breaking the CI assemble task (`mesh_service_example:assembleDebug`)
-and the JitPack publication that consumers may reference, until those are formally retired.
-
-## License
-
-See the root `LICENSE` file.
diff --git a/mesh_service_example/build.gradle.kts b/mesh_service_example/build.gradle.kts
deleted file mode 100644
index 793735dda..000000000
--- a/mesh_service_example/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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 .
- */
-
-import com.android.build.api.dsl.ApplicationExtension
-import org.meshtastic.buildlogic.FlavorDimension
-import org.meshtastic.buildlogic.MeshtasticFlavor
-
-plugins {
- alias(libs.plugins.meshtastic.android.application)
- alias(libs.plugins.meshtastic.android.application.compose)
- alias(libs.plugins.kotlin.parcelize)
- alias(libs.plugins.kotlin.serialization)
-}
-
-configure {
- namespace = "com.meshtastic.android.meshserviceexample"
- defaultConfig {
- // Force this app to use the Google variant of any modules it's using that apply AndroidLibraryConventionPlugin
- missingDimensionStrategy(FlavorDimension.marketplace.name, MeshtasticFlavor.google.name)
- }
-
- testOptions { unitTests.isReturnDefaultValues = true }
-}
-
-dependencies {
- implementation(projects.core.api)
- implementation(projects.core.model)
- implementation(projects.core.proto)
-
- implementation(libs.androidx.activity.compose)
- implementation(libs.jetbrains.lifecycle.viewmodel.compose)
- implementation(libs.jetbrains.lifecycle.runtime)
- implementation(libs.compose.multiplatform.material3)
- implementation(libs.androidx.compose.material.iconsExtended)
- implementation(libs.material)
-
- testImplementation(libs.junit)
- testRuntimeOnly(libs.junit.vintage.engine)
- testImplementation(libs.kotlinx.coroutines.test)
-}
diff --git a/mesh_service_example/detekt-baseline.xml b/mesh_service_example/detekt-baseline.xml
deleted file mode 100644
index ecf2e0cce..000000000
--- a/mesh_service_example/detekt-baseline.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/mesh_service_example/proguard-rules.pro b/mesh_service_example/proguard-rules.pro
deleted file mode 100644
index 481bb4348..000000000
--- a/mesh_service_example/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/mesh_service_example/src/main/AndroidManifest.xml b/mesh_service_example/src/main/AndroidManifest.xml
deleted file mode 100644
index b8ffa4cae..000000000
--- a/mesh_service_example/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
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
deleted file mode 100644
index d61c6f192..000000000
--- a/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainActivity.kt
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * 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 .
- */
-@file:Suppress("DEPRECATION")
-
-package com.meshtastic.android.meshserviceexample
-
-import android.content.BroadcastReceiver
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.content.ServiceConnection
-import android.content.pm.PackageManager
-import android.os.Build
-import android.os.Bundle
-import android.os.IBinder
-import android.util.Log
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.enableEdgeToEdge
-import androidx.activity.viewModels
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.darkColorScheme
-import androidx.compose.material3.dynamicDarkColorScheme
-import androidx.compose.material3.dynamicLightColorScheme
-import androidx.compose.material3.lightColorScheme
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.LocalContext
-import org.meshtastic.core.api.MeshtasticIntent
-import org.meshtastic.core.service.IMeshService
-
-private const val TAG: String = "MeshServiceExample"
-
-/**
- * MainActivity for the MeshServiceExample application.
- *
- * **DEPRECATED.** This entire module (`mesh_service_example`) is scheduled for removal in a future release. Do not use
- * it as a template for new integrations. See `:core:api` README for the current public API surface.
- */
-@Deprecated(
- message =
- "mesh_service_example is deprecated and will be removed in a future release. " +
- "See core/api/README.md for integration guidance.",
-)
-class MainActivity : ComponentActivity() {
-
- private var meshService: IMeshService? = null
- private var isMeshServiceBound = false
-
- private val viewModel: MeshServiceViewModel by viewModels()
-
- private val serviceConnection =
- object : ServiceConnection {
- override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
- meshService = IMeshService.Stub.asInterface(service)
- Log.i(TAG, "Connected to MeshService")
- isMeshServiceBound = true
- viewModel.onServiceConnected(meshService)
- }
-
- override fun onServiceDisconnected(name: ComponentName?) {
- meshService = null
- isMeshServiceBound = false
- viewModel.onServiceDisconnected()
- }
- }
-
- private val meshtasticReceiver =
- object : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- Log.d(TAG, "BroadcastReceiver onReceive: ${intent?.action}")
- intent?.let { viewModel.handleIncomingIntent(it) }
- }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- enableEdgeToEdge()
-
- bindMeshService()
-
- val intentFilter =
- IntentFilter().apply {
- addAction(MeshtasticIntent.ACTION_NODE_CHANGE)
- addAction(MeshtasticIntent.ACTION_CONNECTION_CHANGED)
- addAction(MeshtasticIntent.ACTION_MESH_CONNECTED)
- addAction(MeshtasticIntent.ACTION_MESH_DISCONNECTED)
- addAction(MeshtasticIntent.ACTION_MESSAGE_STATUS)
- addAction(MeshtasticIntent.ACTION_RECEIVED_TEXT_MESSAGE_APP)
- addAction(MeshtasticIntent.ACTION_RECEIVED_POSITION_APP)
- addAction(MeshtasticIntent.ACTION_RECEIVED_TELEMETRY_APP)
- addAction(MeshtasticIntent.ACTION_RECEIVED_NODEINFO_APP)
- addAction(MeshtasticIntent.ACTION_RECEIVED_ATAK_PLUGIN)
- addAction(MeshtasticIntent.ACTION_RECEIVED_ATAK_FORWARDER)
- addAction(MeshtasticIntent.ACTION_RECEIVED_DETECTION_SENSOR_APP)
- addAction(MeshtasticIntent.ACTION_RECEIVED_PRIVATE_APP)
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- registerReceiver(meshtasticReceiver, intentFilter, RECEIVER_EXPORTED)
- } else {
- @Suppress("UnspecifiedRegisterReceiverFlag")
- registerReceiver(meshtasticReceiver, intentFilter)
- }
-
- setContent { ExampleTheme { MainScreen(viewModel) } }
- }
-
- override fun onDestroy() {
- super.onDestroy()
- unregisterReceiver(meshtasticReceiver)
- unbindMeshService()
- }
-
- private fun bindMeshService() {
- try {
- Log.i(TAG, "Attempting to bind to Mesh Service...")
- val intent = Intent("com.geeksville.mesh.Service")
-
- val resolveInfo =
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- packageManager.queryIntentServices(intent, PackageManager.ResolveInfoFlags.of(0))
- } else {
- @Suppress("DEPRECATION")
- packageManager.queryIntentServices(intent, 0)
- }
-
- if (resolveInfo.isNotEmpty()) {
- val serviceInfo = resolveInfo[0].serviceInfo
- intent.setClassName(serviceInfo.packageName, serviceInfo.name)
- Log.i(TAG, "Found service in package: ${serviceInfo.packageName}")
- } else {
- Log.w(TAG, "No service found for action com.geeksville.mesh.Service. Falling back to default.")
- intent.setClassName("com.geeksville.mesh", "org.meshtastic.core.service.MeshService")
- }
-
- val success = bindService(intent, serviceConnection, BIND_AUTO_CREATE)
- if (!success) {
- Log.e(TAG, "bindService returned false")
- }
- } catch (e: SecurityException) {
- Log.e(TAG, "SecurityException while binding: ${e.message}")
- }
- }
-
- private fun unbindMeshService() {
- if (isMeshServiceBound) {
- try {
- unbindService(serviceConnection)
- } catch (e: IllegalArgumentException) {
- Log.w(TAG, "MeshService not registered or already unbound: ${e.message}")
- }
- isMeshServiceBound = false
- meshService = null
- }
- }
-}
-
-@Composable
-fun ExampleTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
- val colorScheme =
- when {
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
- val context = LocalContext.current
- if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
- }
- darkTheme -> darkColorScheme()
- else -> lightColorScheme()
- }
-
- MaterialTheme(colorScheme = colorScheme, content = content)
-}
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
deleted file mode 100644
index 408a37d25..000000000
--- a/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainScreen.kt
+++ /dev/null
@@ -1,585 +0,0 @@
-/*
- * 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 .
- */
-@file:Suppress("TooManyFunctions")
-
-package com.meshtastic.android.meshserviceexample
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.rounded.Message
-import androidx.compose.material.icons.automirrored.rounded.Send
-import androidx.compose.material.icons.rounded.AccountCircle
-import androidx.compose.material.icons.rounded.BatteryUnknown
-import androidx.compose.material.icons.rounded.ExpandLess
-import androidx.compose.material.icons.rounded.ExpandMore
-import androidx.compose.material.icons.rounded.GpsFixed
-import androidx.compose.material.icons.rounded.GpsOff
-import androidx.compose.material.icons.rounded.Hub
-import androidx.compose.material.icons.rounded.Info
-import androidx.compose.material.icons.rounded.MyLocation
-import androidx.compose.material.icons.rounded.PersonSearch
-import androidx.compose.material.icons.rounded.Refresh
-import androidx.compose.material.icons.rounded.RestartAlt
-import androidx.compose.material.icons.rounded.Route
-import androidx.compose.material.icons.rounded.Router
-import androidx.compose.material.icons.rounded.SignalCellularAlt
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.Card
-import androidx.compose.material3.CardDefaults
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.HorizontalDivider
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.SnackbarHost
-import androidx.compose.material3.SnackbarHostState
-import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBar
-import androidx.compose.material3.TopAppBarDefaults
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-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.font.FontFamily
-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.proto.PortNum
-
-@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()
- }
- }
-}
-
-@Composable
-fun SectionHeader(title: String, expanded: Boolean, onExpandClick: () -> Unit, modifier: Modifier = Modifier) {
- Card(
- modifier = modifier.fillMaxWidth().clickable { onExpandClick() },
- colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f)),
- ) {
- Row(
- modifier = Modifier.fillMaxWidth().padding(16.dp),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Text(text = title, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary)
- Icon(
- imageVector = if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore,
- contentDescription = if (expanded) "Collapse" else "Expand",
- tint = MaterialTheme.colorScheme.primary,
- )
- }
- }
-}
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun MainScreen(viewModel: MeshServiceViewModel) {
- val isConnected by viewModel.serviceConnectionStatus.collectAsState()
- val connectionState by viewModel.connectionState.collectAsState()
- val snackbarHostState = remember { SnackbarHostState() }
- val scope = rememberCoroutineScope()
-
- Scaffold(
- modifier = Modifier.fillMaxSize(),
- snackbarHost = { SnackbarHost(snackbarHostState) },
- topBar = {
- TopAppBar(
- title = { TopBarTitle(isConnected, connectionState) },
- actions = {
- IconButton(
- onClick = {
- viewModel.requestNodes()
- scope.launch { snackbarHostState.showSnackbar("Refreshing nodes...") }
- },
- ) {
- Icon(Icons.Rounded.Refresh, contentDescription = "Refresh Nodes")
- }
- },
- colors =
- TopAppBarDefaults.topAppBarColors(
- containerColor = MaterialTheme.colorScheme.surface,
- titleContentColor = MaterialTheme.colorScheme.onSurface,
- ),
- )
- },
- ) { innerPadding ->
- MainContent(viewModel, innerPadding, snackbarHostState)
- }
-}
-
-@Composable
-private fun TopBarTitle(isConnected: Boolean, connectionState: String) {
- Column {
- Text(
- text = "Mesh Service Example",
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- style = MaterialTheme.typography.titleLarge,
- )
- Row(verticalAlignment = Alignment.CenterVertically) {
- val statusColor =
- if (isConnected) {
- Color.Green
- } else {
- MaterialTheme.colorScheme.error
- }
- Box(modifier = Modifier.size(8.dp).clip(CircleShape).background(statusColor))
- Spacer(modifier = Modifier.width(8.dp))
- Text(
- text = if (isConnected) "Connected ($connectionState)" else "Disconnected",
- style = MaterialTheme.typography.labelMedium,
- color = MaterialTheme.colorScheme.onSurfaceVariant,
- )
- }
- }
-}
-
-@Composable
-@Suppress("LongMethod")
-private fun MainContent(
- viewModel: MeshServiceViewModel,
- innerPadding: PaddingValues,
- snackbarHostState: SnackbarHostState,
-) {
- val myNodeInfo by viewModel.myNodeInfo.collectAsState()
- val myId by viewModel.myId.collectAsState()
- val nodes by viewModel.nodes.collectAsState()
- val lastMessage by viewModel.message.collectAsState()
- val packetLog by viewModel.packetLog.collectAsState()
-
- var nodesExpanded by remember { mutableStateOf(false) }
- var logExpanded by remember { mutableStateOf(false) }
- val scope = rememberCoroutineScope()
-
- LazyColumn(
- modifier = Modifier.padding(innerPadding).fillMaxSize(),
- contentPadding = PaddingValues(16.dp),
- verticalArrangement = Arrangement.spacedBy(16.dp),
- ) {
- item { MyInfoSection(myId, myNodeInfo) }
- item { TitledCard(title = "Messaging") { MessagingSection(viewModel, lastMessage) } }
- item { TitledCard(title = "Test Special PortNums") { SpecialAppSection(viewModel) } }
-
- item {
- SectionHeader(
- title = "Mesh Nodes (${nodes.size})",
- expanded = nodesExpanded,
- onExpandClick = { nodesExpanded = !nodesExpanded },
- )
- }
-
- if (nodesExpanded) {
- if (nodes.isEmpty()) {
- item { EmptyNodeState() }
- } else {
- items(nodes) { node ->
- Card(modifier = Modifier.fillMaxWidth()) {
- val nodeLabel = node.user?.longName ?: node.user?.id ?: "Unknown Node"
- NodeItem(node) { action ->
- scope.launch {
- when (action) {
- "traceroute" -> {
- viewModel.requestTraceroute(node.num)
- snackbarHostState.showSnackbar("Traceroute requested for $nodeLabel")
- }
- "telemetry" -> {
- viewModel.requestTelemetry(node.num)
- snackbarHostState.showSnackbar("Telemetry requested for $nodeLabel")
- }
- "neighbors" -> {
- viewModel.requestNeighborInfo(node.num)
- snackbarHostState.showSnackbar("Neighbor info requested for $nodeLabel")
- }
- "position" -> {
- viewModel.requestPosition(node.num)
- snackbarHostState.showSnackbar("Position requested for $nodeLabel")
- }
- "userinfo" -> {
- viewModel.requestUserInfo(node.num)
- snackbarHostState.showSnackbar("User info requested for $nodeLabel")
- }
- "connstatus" -> {
- viewModel.requestDeviceConnectionStatus(node.num)
- snackbarHostState.showSnackbar("Connection status requested for $nodeLabel")
- }
- }
- }
- }
- }
- }
- }
- }
-
- item {
- SectionHeader(title = "Packet Log", expanded = logExpanded, onExpandClick = { logExpanded = !logExpanded })
- }
-
- if (logExpanded) {
- item {
- Card(modifier = Modifier.fillMaxWidth()) {
- Box(modifier = Modifier.padding(16.dp)) { PacketLogContent(packetLog) }
- }
- }
- }
-
- item { ActionButtons(viewModel, snackbarHostState) }
- item { Spacer(modifier = Modifier.height(16.dp)) }
- }
-}
-
-@Composable
-fun SpecialAppSection(viewModel: MeshServiceViewModel) {
- Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
- Button(onClick = { viewModel.sendSpecialPacket(PortNum.ATAK_PLUGIN) }, modifier = Modifier.weight(1f)) {
- Text("Send ATAK")
- }
- Button(
- onClick = { viewModel.sendSpecialPacket(PortNum.DETECTION_SENSOR_APP) },
- modifier = Modifier.weight(1f),
- ) {
- Text("Send Sensor")
- }
- }
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
- Button(onClick = { viewModel.sendSpecialPacket(PortNum.PRIVATE_APP) }, modifier = Modifier.weight(1f)) {
- Text("Send Private")
- }
- }
- }
-}
-
-@Composable
-private fun PacketLogContent(log: List) {
- Column(modifier = Modifier.fillMaxWidth().heightIn(max = 300.dp).verticalScroll(rememberScrollState())) {
- if (log.isEmpty()) {
- Text(
- text = "No packets yet.",
- style = MaterialTheme.typography.bodySmall,
- color = MaterialTheme.colorScheme.onSurfaceVariant,
- modifier = Modifier.padding(vertical = 8.dp),
- )
- } else {
- log.forEach { entry ->
- Text(
- text = entry,
- style = MaterialTheme.typography.bodySmall,
- fontFamily = FontFamily.Monospace,
- modifier = Modifier.padding(vertical = 2.dp),
- )
- HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.2f))
- }
- }
- }
-}
-
-@Composable
-private fun MyInfoSection(myId: String?, myNodeInfo: org.meshtastic.core.model.MyNodeInfo?) {
- TitledCard(title = "My Node Information") {
- ListItem(
- text = "Long ID",
- supportingText = myId ?: "N/A",
- leadingIcon = Icons.Rounded.AccountCircle,
- trailingIcon = null,
- )
- ListItem(
- text = "Firmware",
- supportingText = myNodeInfo?.firmwareString ?: "N/A",
- leadingIcon = Icons.Rounded.Info,
- trailingIcon = null,
- )
- }
-}
-
-@Composable
-private fun EmptyNodeState() {
- Text(
- text = "No mesh nodes discovered yet.",
- style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.onSurfaceVariant,
- modifier = Modifier.fillMaxWidth().padding(vertical = 32.dp),
- textAlign = TextAlign.Center,
- )
-}
-
-@Composable
-fun MessagingSection(viewModel: MeshServiceViewModel, lastMessage: String) {
- var textToSend by remember { mutableStateOf("") }
-
- Column(modifier = Modifier.padding(16.dp)) {
- if (lastMessage.isNotEmpty()) {
- Card(
- modifier = Modifier.fillMaxWidth(),
- colors =
- CardDefaults.cardColors(
- containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f),
- ),
- ) {
- ListItem(
- text = "Last Received",
- supportingText = lastMessage,
- leadingIcon = Icons.AutoMirrored.Rounded.Message,
- trailingIcon = null,
- )
- }
- Spacer(modifier = Modifier.height(12.dp))
- }
- Row(verticalAlignment = Alignment.CenterVertically) {
- OutlinedTextField(
- value = textToSend,
- onValueChange = { textToSend = it },
- modifier = Modifier.weight(1f),
- label = { Text("Send broadcast message") },
- shape = MaterialTheme.shapes.large,
- )
- Spacer(modifier = Modifier.width(8.dp))
- Button(
- onClick = {
- if (textToSend.isNotBlank()) {
- viewModel.sendMessage(textToSend)
- textToSend = ""
- }
- },
- modifier = Modifier.size(56.dp),
- shape = MaterialTheme.shapes.large,
- contentPadding = PaddingValues(0.dp),
- ) {
- Icon(imageVector = Icons.AutoMirrored.Rounded.Send, contentDescription = "Send")
- }
- }
- }
-}
-
-@Composable
-fun NodeItem(node: NodeInfo, onAction: (String) -> Unit) {
- Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
- NodeItemHeader(node)
- Spacer(modifier = Modifier.height(8.dp))
- NodeItemActions(node.isOnline, onAction)
- }
-}
-
-@Composable
-private fun NodeItemHeader(node: NodeInfo) {
- Row(verticalAlignment = Alignment.CenterVertically) {
- Box(contentAlignment = Alignment.BottomEnd) {
- Icon(
- imageVector = Icons.Rounded.AccountCircle,
- contentDescription = null,
- modifier = Modifier.size(48.dp),
- tint = MaterialTheme.colorScheme.outline,
- )
- if (node.isOnline) {
- Box(
- modifier =
- Modifier.size(14.dp)
- .clip(CircleShape)
- .background(MaterialTheme.colorScheme.surface)
- .padding(2.dp)
- .clip(CircleShape)
- .background(Color.Green),
- )
- }
- }
- Spacer(modifier = Modifier.width(16.dp))
- Column(modifier = Modifier.weight(1f)) {
- Text(
- text = node.user?.longName ?: "Unknown Node",
- style = MaterialTheme.typography.titleMedium,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- )
- Text(
- text = "ID: ${node.user?.id ?: "N/A"}",
- style = MaterialTheme.typography.bodySmall,
- color = MaterialTheme.colorScheme.onSurfaceVariant,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- )
- }
- }
-}
-
-@Composable
-private fun NodeItemActions(isOnline: Boolean, onAction: (String) -> Unit) {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.End,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- IconButton(onClick = { onAction("traceroute") }, modifier = Modifier.size(40.dp)) {
- Icon(Icons.Rounded.Route, "Traceroute", Modifier.size(20.dp), MaterialTheme.colorScheme.primary)
- }
- IconButton(onClick = { onAction("telemetry") }, modifier = Modifier.size(40.dp)) {
- Icon(
- @Suppress("DEPRECATION") // AutoMirrored variant not available in current icons version
- Icons.Rounded.BatteryUnknown,
- "Telemetry",
- Modifier.size(20.dp),
- MaterialTheme.colorScheme.secondary,
- )
- }
- IconButton(onClick = { onAction("position") }, modifier = Modifier.size(40.dp)) {
- Icon(Icons.Rounded.MyLocation, "Position", Modifier.size(20.dp), MaterialTheme.colorScheme.tertiary)
- }
- IconButton(onClick = { onAction("neighbors") }, modifier = Modifier.size(40.dp)) {
- Icon(Icons.Rounded.Hub, "Neighbors", Modifier.size(20.dp), MaterialTheme.colorScheme.tertiary)
- }
- IconButton(onClick = { onAction("userinfo") }, modifier = Modifier.size(40.dp)) {
- Icon(Icons.Rounded.PersonSearch, "User Info", Modifier.size(20.dp), MaterialTheme.colorScheme.outline)
- }
- IconButton(onClick = { onAction("connstatus") }, modifier = Modifier.size(40.dp)) {
- Icon(
- Icons.Rounded.SignalCellularAlt,
- "Conn Status",
- Modifier.size(20.dp),
- MaterialTheme.colorScheme.outline,
- )
- }
- if (isOnline) {
- Icon(
- imageVector = Icons.Rounded.Router,
- contentDescription = "Online",
- tint = androidx.compose.ui.graphics.Color.Green.copy(alpha = 0.5f),
- modifier = Modifier.padding(start = 8.dp).size(20.dp),
- )
- }
- }
-}
-
-@Composable
-private fun ActionButtons(viewModel: MeshServiceViewModel, snackbarHostState: SnackbarHostState) {
- val scope = rememberCoroutineScope()
- TitledCard(title = "Device Controls") {
- Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
- GpsButtons(viewModel, snackbarHostState)
- Button(
- modifier = Modifier.fillMaxWidth(),
- onClick = {
- viewModel.rebootLocalDevice()
- scope.launch { snackbarHostState.showSnackbar("Reboot Requested") }
- },
- shape = MaterialTheme.shapes.medium,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.errorContainer,
- contentColor = MaterialTheme.colorScheme.onErrorContainer,
- ),
- ) {
- Icon(imageVector = Icons.Rounded.RestartAlt, contentDescription = null)
- Spacer(modifier = Modifier.width(8.dp))
- Text("Reboot Radio")
- }
- }
- }
-}
-
-@Composable
-private fun GpsButtons(viewModel: MeshServiceViewModel, snackbarHostState: SnackbarHostState) {
- val scope = rememberCoroutineScope()
- val colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.secondaryContainer,
- contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
- )
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
- Button(
- modifier = Modifier.weight(1f),
- onClick = {
- viewModel.startProvideLocation()
- scope.launch { snackbarHostState.showSnackbar("GPS Sharing Started") }
- },
- shape = MaterialTheme.shapes.medium,
- colors = colors,
- ) {
- Icon(imageVector = Icons.Rounded.GpsFixed, contentDescription = null)
- Spacer(modifier = Modifier.width(8.dp))
- Text("Start GPS", style = MaterialTheme.typography.labelLarge)
- }
- Button(
- modifier = Modifier.weight(1f),
- onClick = {
- viewModel.stopProvideLocation()
- scope.launch { snackbarHostState.showSnackbar("GPS Sharing Stopped") }
- },
- shape = MaterialTheme.shapes.medium,
- colors = colors,
- ) {
- Icon(imageVector = Icons.Rounded.GpsOff, contentDescription = null)
- Spacer(modifier = Modifier.width(8.dp))
- Text("Stop GPS", style = MaterialTheme.typography.labelLarge)
- }
- }
-}
diff --git a/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MeshServiceViewModel.kt b/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MeshServiceViewModel.kt
deleted file mode 100644
index 7c72516bf..000000000
--- a/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MeshServiceViewModel.kt
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * 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 .
- */
-@file:Suppress("DEPRECATION") // IMeshService is deprecated but still required for AIDL binding
-
-package com.meshtastic.android.meshserviceexample
-
-import android.content.Intent
-import android.os.Build
-import android.os.Parcelable
-import android.os.RemoteException
-import android.util.Log
-import androidx.lifecycle.ViewModel
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import okio.ByteString.Companion.toByteString
-import org.meshtastic.core.common.util.nowMillis
-import org.meshtastic.core.common.util.toDate
-import org.meshtastic.core.common.util.toInstant
-import org.meshtastic.core.model.DataPacket
-import org.meshtastic.core.model.MessageStatus
-import org.meshtastic.core.model.MyNodeInfo
-import org.meshtastic.core.model.NodeInfo
-import org.meshtastic.core.model.Position
-import org.meshtastic.core.service.IMeshService
-import org.meshtastic.proto.PortNum
-import java.text.SimpleDateFormat
-import java.util.Locale
-import kotlin.random.Random
-
-private const val TAG = "MeshServiceViewModel"
-
-/** ViewModel for MeshServiceExample. Handles interaction with IMeshService AIDL and manages UI state. */
-@Suppress("TooManyFunctions")
-class MeshServiceViewModel : ViewModel() {
-
- private var meshService: IMeshService? = null
-
- private val _myNodeInfo = MutableStateFlow(null)
- val myNodeInfo: StateFlow = _myNodeInfo.asStateFlow()
-
- private val _myId = MutableStateFlow(null)
- val myId: StateFlow = _myId.asStateFlow()
-
- private val _nodes = MutableStateFlow>(emptyList())
- val nodes: StateFlow> = _nodes.asStateFlow()
-
- private val _serviceConnectionStatus = MutableStateFlow(false)
- val serviceConnectionStatus: StateFlow = _serviceConnectionStatus.asStateFlow()
-
- private val _message = MutableStateFlow("")
- val message: StateFlow = _message.asStateFlow()
-
- private val _connectionState = MutableStateFlow("UNKNOWN")
- val connectionState: StateFlow = _connectionState.asStateFlow()
-
- private val _packetLog = MutableStateFlow>(emptyList())
- val packetLog: StateFlow> = _packetLog.asStateFlow()
-
- fun onServiceConnected(service: IMeshService?) {
- meshService = service
- _serviceConnectionStatus.value = true
- updateAllData()
- addToLog("Service Connected")
- }
-
- fun onServiceDisconnected() {
- meshService = null
- _serviceConnectionStatus.value = false
- addToLog("Service Disconnected")
- }
-
- private fun updateAllData() {
- requestMyNodeInfo()
- requestNodes()
- updateConnectionState()
- updateMyId()
- }
-
- fun updateMyId() {
- meshService?.let {
- try {
- _myId.value = it.myId
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to get MyId", e)
- }
- }
- }
-
- fun updateConnectionState() {
- meshService?.let {
- try {
- val state = it.connectionState() ?: "UNKNOWN"
- _connectionState.value = state
- addToLog("Connection State: $state")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to get connection state", e)
- }
- }
- }
-
- fun sendMessage(text: String) {
- meshService?.let { service ->
- try {
- val packet =
- DataPacket(
- to = DataPacket.ID_BROADCAST,
- bytes = text.encodeToByteArray().toByteString(),
- dataType = PortNum.TEXT_MESSAGE_APP.value,
- from = DataPacket.ID_LOCAL,
- time = nowMillis,
- id = service.packetId,
- status = MessageStatus.UNKNOWN,
- hopLimit = 3,
- channel = 0,
- wantAck = true,
- )
- service.send(packet)
- Log.d(TAG, "Message sent successfully, assigned ID: ${packet.id}")
- addToLog("Sent: $text (ID: ${packet.id})")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to send message", e)
- addToLog("Failed to send message: ${e.message}")
- }
- } ?: Log.w(TAG, "MeshService is not bound, cannot send message")
- }
-
- fun sendSpecialPacket(portNum: PortNum) {
- meshService?.let { service ->
- try {
- val packet =
- DataPacket(
- to = DataPacket.ID_BROADCAST,
- bytes = "Special Payload for ${portNum.name}".encodeToByteArray().toByteString(),
- dataType = portNum.value,
- from = DataPacket.ID_LOCAL,
- time = nowMillis,
- id = service.packetId,
- status = MessageStatus.UNKNOWN,
- hopLimit = 3,
- channel = 0,
- wantAck = true,
- )
- service.send(packet)
- addToLog("Sent ${portNum.name} Packet (ID: ${packet.id})")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to send special packet", e)
- addToLog("Failed to send ${portNum.name} packet: ${e.message}")
- }
- }
- }
-
- fun requestMyNodeInfo() {
- meshService?.let {
- try {
- _myNodeInfo.value = it.myNodeInfo
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to get MyNodeInfo", e)
- }
- }
- }
-
- fun requestNodes() {
- meshService?.let {
- try {
- _nodes.value = it.nodes ?: emptyList()
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to get nodes", e)
- }
- }
- }
-
- fun startProvideLocation() {
- try {
- meshService?.startProvideLocation()
- addToLog("Started GPS sharing")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to start providing location", e)
- }
- }
-
- fun stopProvideLocation() {
- try {
- meshService?.stopProvideLocation()
- addToLog("Stopped GPS sharing")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to stop providing location", e)
- }
- }
-
- fun requestTraceroute(nodeNum: Int) {
- meshService?.let {
- try {
- it.requestTraceroute(Random.nextInt(), nodeNum)
- Log.i(TAG, "Traceroute requested for node $nodeNum")
- addToLog("Requested Traceroute for $nodeNum")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to request traceroute", e)
- }
- }
- }
-
- fun requestTelemetry(nodeNum: Int) {
- meshService?.let {
- try {
- it.requestTelemetry(Random.nextInt(), nodeNum, 1)
- Log.i(TAG, "Telemetry requested for node $nodeNum")
- addToLog("Requested Telemetry for $nodeNum")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to request telemetry", e)
- }
- }
- }
-
- fun requestNeighborInfo(nodeNum: Int) {
- meshService?.let {
- try {
- it.requestNeighborInfo(Random.nextInt(), nodeNum)
- Log.i(TAG, "Neighbor info requested for node $nodeNum")
- addToLog("Requested Neighbors for $nodeNum")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to request neighbor info", e)
- }
- }
- }
-
- fun requestPosition(nodeNum: Int) {
- meshService?.let {
- try {
- it.requestPosition(nodeNum, Position(0.0, 0.0, 0))
- Log.i(TAG, "Position requested for node $nodeNum")
- addToLog("Requested Position for $nodeNum")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to request position", e)
- }
- }
- }
-
- fun requestUserInfo(nodeNum: Int) {
- meshService?.let {
- try {
- it.requestUserInfo(nodeNum)
- Log.i(TAG, "User info requested for node $nodeNum")
- addToLog("Requested User Info for $nodeNum")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to request user info", e)
- }
- }
- }
-
- fun requestDeviceConnectionStatus(nodeNum: Int) {
- meshService?.let {
- try {
- it.getDeviceConnectionStatus(Random.nextInt(), nodeNum)
- Log.i(TAG, "Device connection status requested for node $nodeNum")
- addToLog("Requested Connection Status for $nodeNum")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to request device connection status", e)
- }
- }
- }
-
- fun rebootLocalDevice() {
- meshService?.let {
- try {
- it.requestReboot(Random.nextInt(), 0)
- Log.w(TAG, "Local reboot requested!")
- addToLog("Requested Local Reboot")
- } catch (e: RemoteException) {
- Log.e(TAG, "Failed to request reboot", e)
- }
- }
- }
-
- fun handleIncomingIntent(intent: Intent) {
- val action = intent.action ?: return
- Log.d(TAG, "Received broadcast: $action")
-
- when (action) {
- "com.geeksville.mesh.NODE_CHANGE" -> handleNodeChange(intent)
- "com.geeksville.mesh.CONNECTION_CHANGED",
- "com.geeksville.mesh.MESH_CONNECTED",
- "com.geeksville.mesh.MESH_DISCONNECTED",
- -> updateConnectionState()
-
- "com.geeksville.mesh.MESSAGE_STATUS" -> handleMessageStatus(intent)
- else ->
- if (action.startsWith("com.geeksville.mesh.RECEIVED.")) {
- handleReceivedPacket(action, intent)
- }
- }
- }
-
- private fun handleNodeChange(intent: Intent) {
- val nodeInfo = intent.getParcelableCompat("com.geeksville.mesh.NodeInfo", NodeInfo::class.java)
- nodeInfo?.let { ni ->
- Log.d(TAG, "Node updated: ${ni.num}")
- _nodes.value =
- _nodes.value.toMutableList().apply {
- val index = indexOfFirst { it.num == ni.num }
- if (index != -1) set(index, ni) else add(ni)
- }
- }
- }
-
- private fun handleMessageStatus(intent: Intent) {
- val id = intent.getIntExtra("com.geeksville.mesh.PacketId", 0)
- val status = intent.getParcelableCompat("com.geeksville.mesh.Status", MessageStatus::class.java)
- Log.d(TAG, "Message Status for ID $id: $status")
- addToLog("Msg Status ID $id: $status")
- }
-
- private fun handleReceivedPacket(action: String, intent: Intent) {
- val packet = intent.getParcelableCompat("com.geeksville.mesh.Payload", DataPacket::class.java)
- if (packet == null) {
- Log.e(TAG, "Received packet extra was NULL for action: $action")
- addToLog("Error: Packet payload was null for $action")
- return
- }
-
- Log.d(TAG, "Packet received: $packet")
-
- if (packet.dataType == PortNum.TEXT_MESSAGE_APP.value) {
- val receivedText = packet.bytes?.utf8() ?: ""
- _message.value = "From ${packet.from}: $receivedText"
- addToLog("Received Text from ${packet.from}: $receivedText")
- } else {
- val type = action.substringAfterLast(".")
- addToLog("Received $type from ${packet.from}. Check Logcat for details.")
- }
- }
-
- private fun addToLog(entry: String) {
- val date = nowMillis.toInstant().toDate()
- val timestamp = SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(date)
- val logEntry = "[$timestamp] $entry"
- Log.d(TAG, "Log: $logEntry")
- @Suppress("MagicNumber")
- _packetLog.value = (listOf(logEntry) + _packetLog.value).take(50)
- }
-
- private fun Intent.getParcelableCompat(key: String, clazz: Class): T? =
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- getParcelableExtra(key, clazz)
- } else {
- @Suppress("DEPRECATION")
- getParcelableExtra(key)
- }
-}
diff --git a/mesh_service_example/src/main/res/drawable-anydpi/ic_launcher_background.xml b/mesh_service_example/src/main/res/drawable-anydpi/ic_launcher_background.xml
deleted file mode 100644
index 07d5da9cb..000000000
--- a/mesh_service_example/src/main/res/drawable-anydpi/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/mesh_service_example/src/main/res/drawable-anydpi/ic_launcher_foreground.xml b/mesh_service_example/src/main/res/drawable-anydpi/ic_launcher_foreground.xml
deleted file mode 100644
index 2b068d114..000000000
--- a/mesh_service_example/src/main/res/drawable-anydpi/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mesh_service_example/src/main/res/mipmap-anydpi/ic_launcher.xml b/mesh_service_example/src/main/res/mipmap-anydpi/ic_launcher.xml
deleted file mode 100644
index 6f3b755bf..000000000
--- a/mesh_service_example/src/main/res/mipmap-anydpi/ic_launcher.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mesh_service_example/src/main/res/values-ar-rSA/strings.xml b/mesh_service_example/src/main/res/values-ar-rSA/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-ar-rSA/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-b+sr+Latn/strings.xml b/mesh_service_example/src/main/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-be-rBY/strings.xml b/mesh_service_example/src/main/res/values-be-rBY/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-be-rBY/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-bg-rBG/strings.xml b/mesh_service_example/src/main/res/values-bg-rBG/strings.xml
deleted file mode 100644
index bebf8fbdd..000000000
--- a/mesh_service_example/src/main/res/values-bg-rBG/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Изпратете съобщение за здравей
-
diff --git a/mesh_service_example/src/main/res/values-ca-rES/strings.xml b/mesh_service_example/src/main/res/values-ca-rES/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-ca-rES/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-cs-rCZ/strings.xml b/mesh_service_example/src/main/res/values-cs-rCZ/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-cs-rCZ/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-de-rDE/strings.xml b/mesh_service_example/src/main/res/values-de-rDE/strings.xml
deleted file mode 100644
index 968230ec2..000000000
--- a/mesh_service_example/src/main/res/values-de-rDE/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- Beispiel MeshService
- Hallo Nachricht senden
-
diff --git a/mesh_service_example/src/main/res/values-el-rGR/strings.xml b/mesh_service_example/src/main/res/values-el-rGR/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-el-rGR/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-es-rES/strings.xml b/mesh_service_example/src/main/res/values-es-rES/strings.xml
deleted file mode 100644
index 8abd298f5..000000000
--- a/mesh_service_example/src/main/res/values-es-rES/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- Ejemplo de servicio de red
- Enviar Mensaje Hola
-
diff --git a/mesh_service_example/src/main/res/values-et-rEE/strings.xml b/mesh_service_example/src/main/res/values-et-rEE/strings.xml
deleted file mode 100644
index dd6ff8304..000000000
--- a/mesh_service_example/src/main/res/values-et-rEE/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceNäidis
- Saada Tere sõnum
-
diff --git a/mesh_service_example/src/main/res/values-fi-rFI/strings.xml b/mesh_service_example/src/main/res/values-fi-rFI/strings.xml
deleted file mode 100644
index 2da506dda..000000000
--- a/mesh_service_example/src/main/res/values-fi-rFI/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExamplebled
- Lähetä tervehdysviesti
-
diff --git a/mesh_service_example/src/main/res/values-fr-rFR/strings.xml b/mesh_service_example/src/main/res/values-fr-rFR/strings.xml
deleted file mode 100644
index 2b9ff6e40..000000000
--- a/mesh_service_example/src/main/res/values-fr-rFR/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- Exemple de service de maillage
- Envoyer un message d’annonce
-
diff --git a/mesh_service_example/src/main/res/values-ga-rIE/strings.xml b/mesh_service_example/src/main/res/values-ga-rIE/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-ga-rIE/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-gl-rES/strings.xml b/mesh_service_example/src/main/res/values-gl-rES/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-gl-rES/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-hr-rHR/strings.xml b/mesh_service_example/src/main/res/values-hr-rHR/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-hr-rHR/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-ht-rHT/strings.xml b/mesh_service_example/src/main/res/values-ht-rHT/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-ht-rHT/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-hu-rHU/strings.xml b/mesh_service_example/src/main/res/values-hu-rHU/strings.xml
deleted file mode 100644
index 1cff8d920..000000000
--- a/mesh_service_example/src/main/res/values-hu-rHU/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Hello Üzenet Küldés
-
diff --git a/mesh_service_example/src/main/res/values-is-rIS/strings.xml b/mesh_service_example/src/main/res/values-is-rIS/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-is-rIS/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-it-rIT/strings.xml b/mesh_service_example/src/main/res/values-it-rIT/strings.xml
deleted file mode 100644
index dd7addd1d..000000000
--- a/mesh_service_example/src/main/res/values-it-rIT/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Invia Messaggio di Saluto
-
diff --git a/mesh_service_example/src/main/res/values-iw-rIL/strings.xml b/mesh_service_example/src/main/res/values-iw-rIL/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-iw-rIL/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-ja-rJP/strings.xml b/mesh_service_example/src/main/res/values-ja-rJP/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-ja-rJP/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-ko-rKR/strings.xml b/mesh_service_example/src/main/res/values-ko-rKR/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-ko-rKR/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-lt-rLT/strings.xml b/mesh_service_example/src/main/res/values-lt-rLT/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-lt-rLT/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-nl-rNL/strings.xml b/mesh_service_example/src/main/res/values-nl-rNL/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-nl-rNL/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-no-rNO/strings.xml b/mesh_service_example/src/main/res/values-no-rNO/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-no-rNO/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-pl-rPL/strings.xml b/mesh_service_example/src/main/res/values-pl-rPL/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-pl-rPL/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-pt-rBR/strings.xml b/mesh_service_example/src/main/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 4e232be75..000000000
--- a/mesh_service_example/src/main/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- ExemploServiçoMesh
- Enviar Mensagem de Olá
-
diff --git a/mesh_service_example/src/main/res/values-pt-rPT/strings.xml b/mesh_service_example/src/main/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-ro-rRO/strings.xml b/mesh_service_example/src/main/res/values-ro-rRO/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-ro-rRO/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-ru-rRU/strings.xml b/mesh_service_example/src/main/res/values-ru-rRU/strings.xml
deleted file mode 100644
index ba088c7e3..000000000
--- a/mesh_service_example/src/main/res/values-ru-rRU/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Отправить приветственное сообщение
-
diff --git a/mesh_service_example/src/main/res/values-sk-rSK/strings.xml b/mesh_service_example/src/main/res/values-sk-rSK/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-sk-rSK/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-sl-rSI/strings.xml b/mesh_service_example/src/main/res/values-sl-rSI/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-sl-rSI/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-sq-rAL/strings.xml b/mesh_service_example/src/main/res/values-sq-rAL/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-sq-rAL/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-srp/strings.xml b/mesh_service_example/src/main/res/values-srp/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-srp/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-sv-rSE/strings.xml b/mesh_service_example/src/main/res/values-sv-rSE/strings.xml
deleted file mode 100644
index f9271ce44..000000000
--- a/mesh_service_example/src/main/res/values-sv-rSE/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- Mesh-service exempel
- Skicka Hej-meddelande
-
diff --git a/mesh_service_example/src/main/res/values-tr-rTR/strings.xml b/mesh_service_example/src/main/res/values-tr-rTR/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-tr-rTR/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-uk-rUA/strings.xml b/mesh_service_example/src/main/res/values-uk-rUA/strings.xml
deleted file mode 100644
index 37d7a2bb2..000000000
--- a/mesh_service_example/src/main/res/values-uk-rUA/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Надіслати привітальне повідомлення
-
diff --git a/mesh_service_example/src/main/res/values-zh-rCN/strings.xml b/mesh_service_example/src/main/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 30fbd6de5..000000000
--- a/mesh_service_example/src/main/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- MeshServiceExample
- Send Hello Message
-
diff --git a/mesh_service_example/src/main/res/values-zh-rTW/strings.xml b/mesh_service_example/src/main/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 16c04c5d3..000000000
--- a/mesh_service_example/src/main/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- Mesh 服務範例
- 發送打招呼訊息
-
diff --git a/mesh_service_example/src/main/res/values/colors.xml b/mesh_service_example/src/main/res/values/colors.xml
deleted file mode 100644
index a6b3daec9..000000000
--- a/mesh_service_example/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/mesh_service_example/src/main/res/values/strings.xml b/mesh_service_example/src/main/res/values/strings.xml
deleted file mode 100644
index e194d4b9b..000000000
--- a/mesh_service_example/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
- MeshServiceExample
-
diff --git a/mesh_service_example/src/main/res/values/themes.xml b/mesh_service_example/src/main/res/values/themes.xml
deleted file mode 100644
index e8f8fe799..000000000
--- a/mesh_service_example/src/main/res/values/themes.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mesh_service_example/src/main/res/xml/backup_rules.xml b/mesh_service_example/src/main/res/xml/backup_rules.xml
deleted file mode 100644
index 4df925582..000000000
--- a/mesh_service_example/src/main/res/xml/backup_rules.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/mesh_service_example/src/main/res/xml/data_extraction_rules.xml b/mesh_service_example/src/main/res/xml/data_extraction_rules.xml
deleted file mode 100644
index 9ee9997b0..000000000
--- a/mesh_service_example/src/main/res/xml/data_extraction_rules.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 656d6f831..f9664baaa 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -47,7 +47,6 @@ include(
":feature:firmware",
":feature:wifi-provision",
":feature:widget",
- ":mesh_service_example",
":desktop",
)
rootProject.name = "MeshtasticAndroid"