From 401f59489a733330f2b1b9a27ebbbb1661bc34a2 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Tue, 14 Apr 2026 22:10:23 -0500 Subject: [PATCH] chore: remove deprecated mesh_service_example module (#5055) --- .github/workflows/pull-request.yml | 3 +- .skills/project-overview/SKILL.md | 1 - .skills/testing-ci/SKILL.md | 2 +- codecov.yml | 4 - .../core/service/ServiceBroadcasts.kt | 2 +- docs/BUILD_LOGIC_CONVENTIONS_GUIDE.md | 4 +- gradle/libs.versions.toml | 1 - mesh_service_example/README.md | 20 - mesh_service_example/build.gradle.kts | 54 -- mesh_service_example/detekt-baseline.xml | 5 - mesh_service_example/proguard-rules.pro | 21 - .../src/main/AndroidManifest.xml | 33 - .../meshserviceexample/MainActivity.kt | 187 ------ .../android/meshserviceexample/MainScreen.kt | 585 ------------------ .../MeshServiceViewModel.kt | 363 ----------- .../ic_launcher_background.xml | 170 ----- .../ic_launcher_foreground.xml | 30 - .../main/res/mipmap-anydpi/ic_launcher.xml | 6 - .../src/main/res/values-ar-rSA/strings.xml | 21 - .../src/main/res/values-b+sr+Latn/strings.xml | 21 - .../src/main/res/values-be-rBY/strings.xml | 21 - .../src/main/res/values-bg-rBG/strings.xml | 21 - .../src/main/res/values-ca-rES/strings.xml | 21 - .../src/main/res/values-cs-rCZ/strings.xml | 21 - .../src/main/res/values-de-rDE/strings.xml | 21 - .../src/main/res/values-el-rGR/strings.xml | 21 - .../src/main/res/values-es-rES/strings.xml | 21 - .../src/main/res/values-et-rEE/strings.xml | 21 - .../src/main/res/values-fi-rFI/strings.xml | 21 - .../src/main/res/values-fr-rFR/strings.xml | 21 - .../src/main/res/values-ga-rIE/strings.xml | 21 - .../src/main/res/values-gl-rES/strings.xml | 21 - .../src/main/res/values-hr-rHR/strings.xml | 21 - .../src/main/res/values-ht-rHT/strings.xml | 21 - .../src/main/res/values-hu-rHU/strings.xml | 21 - .../src/main/res/values-is-rIS/strings.xml | 21 - .../src/main/res/values-it-rIT/strings.xml | 21 - .../src/main/res/values-iw-rIL/strings.xml | 21 - .../src/main/res/values-ja-rJP/strings.xml | 21 - .../src/main/res/values-ko-rKR/strings.xml | 21 - .../src/main/res/values-lt-rLT/strings.xml | 21 - .../src/main/res/values-nl-rNL/strings.xml | 21 - .../src/main/res/values-no-rNO/strings.xml | 21 - .../src/main/res/values-pl-rPL/strings.xml | 21 - .../src/main/res/values-pt-rBR/strings.xml | 21 - .../src/main/res/values-pt-rPT/strings.xml | 21 - .../src/main/res/values-ro-rRO/strings.xml | 21 - .../src/main/res/values-ru-rRU/strings.xml | 21 - .../src/main/res/values-sk-rSK/strings.xml | 21 - .../src/main/res/values-sl-rSI/strings.xml | 21 - .../src/main/res/values-sq-rAL/strings.xml | 21 - .../src/main/res/values-srp/strings.xml | 21 - .../src/main/res/values-sv-rSE/strings.xml | 21 - .../src/main/res/values-tr-rTR/strings.xml | 21 - .../src/main/res/values-uk-rUA/strings.xml | 21 - .../src/main/res/values-zh-rCN/strings.xml | 21 - .../src/main/res/values-zh-rTW/strings.xml | 21 - .../src/main/res/values/colors.xml | 2 - .../src/main/res/values/strings.xml | 21 - .../src/main/res/values/themes.xml | 9 - .../src/main/res/xml/backup_rules.xml | 13 - .../main/res/xml/data_extraction_rules.xml | 19 - settings.gradle.kts | 1 - 63 files changed, 5 insertions(+), 2370 deletions(-) delete mode 100644 mesh_service_example/README.md delete mode 100644 mesh_service_example/build.gradle.kts delete mode 100644 mesh_service_example/detekt-baseline.xml delete mode 100644 mesh_service_example/proguard-rules.pro delete mode 100644 mesh_service_example/src/main/AndroidManifest.xml delete mode 100644 mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainActivity.kt delete mode 100644 mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainScreen.kt delete mode 100644 mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MeshServiceViewModel.kt delete mode 100644 mesh_service_example/src/main/res/drawable-anydpi/ic_launcher_background.xml delete mode 100644 mesh_service_example/src/main/res/drawable-anydpi/ic_launcher_foreground.xml delete mode 100644 mesh_service_example/src/main/res/mipmap-anydpi/ic_launcher.xml delete mode 100644 mesh_service_example/src/main/res/values-ar-rSA/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-b+sr+Latn/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-be-rBY/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-bg-rBG/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-ca-rES/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-cs-rCZ/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-de-rDE/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-el-rGR/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-es-rES/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-et-rEE/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-fi-rFI/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-fr-rFR/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-ga-rIE/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-gl-rES/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-hr-rHR/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-ht-rHT/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-hu-rHU/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-is-rIS/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-it-rIT/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-iw-rIL/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-ja-rJP/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-ko-rKR/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-lt-rLT/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-nl-rNL/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-no-rNO/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-pl-rPL/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-pt-rBR/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-pt-rPT/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-ro-rRO/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-ru-rRU/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-sk-rSK/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-sl-rSI/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-sq-rAL/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-srp/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-sv-rSE/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-tr-rTR/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-uk-rUA/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-zh-rCN/strings.xml delete mode 100644 mesh_service_example/src/main/res/values-zh-rTW/strings.xml delete mode 100644 mesh_service_example/src/main/res/values/colors.xml delete mode 100644 mesh_service_example/src/main/res/values/strings.xml delete mode 100644 mesh_service_example/src/main/res/values/themes.xml delete mode 100644 mesh_service_example/src/main/res/xml/backup_rules.xml delete mode 100644 mesh_service_example/src/main/res/xml/data_extraction_rules.xml 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 @@ - - - - -