mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
chore: remove deprecated mesh_service_example module (#5055)
This commit is contained in:
parent
a2763bdfeb
commit
401f59489a
63 changed files with 5 additions and 2370 deletions
3
.github/workflows/pull-request.yml
vendored
3
.github/workflows/pull-request.yml
vendored
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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`).
|
||||
|
|
|
|||
|
|
@ -57,10 +57,6 @@ component_management:
|
|||
name: Desktop
|
||||
paths:
|
||||
- desktop/**
|
||||
- component_id: example
|
||||
name: Example
|
||||
paths:
|
||||
- mesh_service_example/**
|
||||
|
||||
ignore:
|
||||
- "**/build/**"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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`).
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<ApplicationExtension> {
|
||||
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)
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues/>
|
||||
</SmellBaseline>
|
||||
21
mesh_service_example/proguard-rules.pro
vendored
21
mesh_service_example/proguard-rules.pro
vendored
|
|
@ -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
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="com.geeksville.mesh.permission.BIND_MESH_SERVICE" />
|
||||
<queries>
|
||||
<package android:name="com.geeksville.mesh" />
|
||||
<package android:name="com.geeksville.mesh.google.debug" />
|
||||
<package android:name="com.geeksville.mesh.fdroid.debug" />
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.MeshServiceExample"
|
||||
tools:targetApi="31"
|
||||
tools:replace="android:allowBackup">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@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)
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@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<String>) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@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<MyNodeInfo?>(null)
|
||||
val myNodeInfo: StateFlow<MyNodeInfo?> = _myNodeInfo.asStateFlow()
|
||||
|
||||
private val _myId = MutableStateFlow<String?>(null)
|
||||
val myId: StateFlow<String?> = _myId.asStateFlow()
|
||||
|
||||
private val _nodes = MutableStateFlow<List<NodeInfo>>(emptyList())
|
||||
val nodes: StateFlow<List<NodeInfo>> = _nodes.asStateFlow()
|
||||
|
||||
private val _serviceConnectionStatus = MutableStateFlow(false)
|
||||
val serviceConnectionStatus: StateFlow<Boolean> = _serviceConnectionStatus.asStateFlow()
|
||||
|
||||
private val _message = MutableStateFlow("")
|
||||
val message: StateFlow<String> = _message.asStateFlow()
|
||||
|
||||
private val _connectionState = MutableStateFlow("UNKNOWN")
|
||||
val connectionState: StateFlow<String> = _connectionState.asStateFlow()
|
||||
|
||||
private val _packetLog = MutableStateFlow<List<String>>(emptyList())
|
||||
val packetLog: StateFlow<List<String>> = _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 <T : Parcelable> Intent.getParcelableCompat(key: String, clazz: Class<T>): T? =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
getParcelableExtra(key, clazz)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
getParcelableExtra(key)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Изпратете съобщение за здравей</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">Beispiel MeshService</string>
|
||||
<string name="send_hello_message">Hallo Nachricht senden</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">Ejemplo de servicio de red</string>
|
||||
<string name="send_hello_message">Enviar Mensaje Hola</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceNäidis</string>
|
||||
<string name="send_hello_message">Saada Tere sõnum</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExamplebled</string>
|
||||
<string name="send_hello_message">Lähetä tervehdysviesti</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">Exemple de service de maillage</string>
|
||||
<string name="send_hello_message">Envoyer un message d’annonce</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Hello Üzenet Küldés</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Invia Messaggio di Saluto</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">ExemploServiçoMesh</string>
|
||||
<string name="send_hello_message">Enviar Mensagem de Olá</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Отправить приветственное сообщение</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">Mesh-service exempel</string>
|
||||
<string name="send_hello_message">Skicka Hej-meddelande</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Надіслати привітальне повідомлення</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
<string name="send_hello_message">Send Hello Message</string>
|
||||
</resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">Mesh 服務範例</string>
|
||||
<string name="send_hello_message">發送打招呼訊息</string>
|
||||
</resources>
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2025 Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_name">MeshServiceExample</string>
|
||||
</resources>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Base.Theme.MeshServiceExample" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Customize your light theme here. -->
|
||||
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
||||
</style>
|
||||
|
||||
<style name="Theme.MeshServiceExample" parent="Base.Theme.MeshServiceExample" />
|
||||
</resources>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older than API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
||||
|
|
@ -47,7 +47,6 @@ include(
|
|||
":feature:firmware",
|
||||
":feature:wifi-provision",
|
||||
":feature:widget",
|
||||
":mesh_service_example",
|
||||
":desktop",
|
||||
)
|
||||
rootProject.name = "MeshtasticAndroid"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue