mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-03-30 19:54:27 -04:00
feat: service extraction (#4828)
This commit is contained in:
@@ -152,7 +152,7 @@
|
||||
|
||||
<!-- This is the public API for doing mesh radio operations from android apps -->
|
||||
<service
|
||||
android:name="org.meshtastic.app.service.MeshService"
|
||||
android:name="org.meshtastic.core.service.MeshService"
|
||||
android:enabled="true"
|
||||
android:foregroundServiceType="connectedDevice|location"
|
||||
android:exported="true" tools:ignore="ExportedActivity">
|
||||
@@ -228,7 +228,7 @@
|
||||
android:resource="@xml/device_filter" />
|
||||
</activity>
|
||||
|
||||
<receiver android:name="org.meshtastic.app.service.BootCompleteReceiver"
|
||||
<receiver android:name="org.meshtastic.core.service.BootCompleteReceiver"
|
||||
android:exported="false">
|
||||
<!-- handle boot events -->
|
||||
<intent-filter>
|
||||
@@ -252,9 +252,9 @@
|
||||
android:path="com.geeksville.mesh" /> -->
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name="org.meshtastic.app.service.ReplyReceiver" android:exported="false" />
|
||||
<receiver android:name="org.meshtastic.app.service.MarkAsReadReceiver" android:exported="false" />
|
||||
<receiver android:name="org.meshtastic.app.service.ReactionReceiver" android:exported="false" />
|
||||
<receiver android:name="org.meshtastic.core.service.ReplyReceiver" android:exported="false" />
|
||||
<receiver android:name="org.meshtastic.core.service.MarkAsReadReceiver" android:exported="false" />
|
||||
<receiver android:name="org.meshtastic.core.service.ReactionReceiver" android:exported="false" />
|
||||
|
||||
<receiver
|
||||
android:name="org.meshtastic.app.widget.LocalStatsWidgetReceiver"
|
||||
|
||||
@@ -25,13 +25,13 @@ import androidx.lifecycle.lifecycleScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.annotation.Factory
|
||||
import org.meshtastic.app.service.MeshService
|
||||
import org.meshtastic.app.service.startService
|
||||
import org.meshtastic.core.common.util.SequentialJob
|
||||
import org.meshtastic.core.service.AndroidServiceRepository
|
||||
import org.meshtastic.core.service.BindFailedException
|
||||
import org.meshtastic.core.service.IMeshService
|
||||
import org.meshtastic.core.service.MeshService
|
||||
import org.meshtastic.core.service.ServiceClient
|
||||
import org.meshtastic.core.service.startService
|
||||
|
||||
/** A Activity-lifecycle-aware [ServiceClient] that binds [MeshService] once the Activity is started. */
|
||||
@Factory
|
||||
|
||||
@@ -40,10 +40,10 @@ import org.koin.core.context.startKoin
|
||||
import org.meshtastic.app.di.AppKoinModule
|
||||
import org.meshtastic.app.di.module
|
||||
import org.meshtastic.app.widget.LocalStatsWidgetReceiver
|
||||
import org.meshtastic.app.worker.MeshLogCleanupWorker
|
||||
import org.meshtastic.core.common.ContextServices
|
||||
import org.meshtastic.core.database.DatabaseManager
|
||||
import org.meshtastic.core.repository.MeshPrefs
|
||||
import org.meshtastic.core.service.worker.MeshLogCleanupWorker
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.toJavaDuration
|
||||
|
||||
@@ -37,14 +37,15 @@ import org.meshtastic.core.database.di.CoreDatabaseAndroidModule
|
||||
import org.meshtastic.core.database.di.CoreDatabaseModule
|
||||
import org.meshtastic.core.datastore.di.CoreDatastoreAndroidModule
|
||||
import org.meshtastic.core.datastore.di.CoreDatastoreModule
|
||||
import org.meshtastic.core.network.di.CoreNetworkAndroidModule
|
||||
import org.meshtastic.core.network.di.CoreNetworkModule
|
||||
import org.meshtastic.core.network.repository.ProbeTableProvider
|
||||
import org.meshtastic.core.prefs.di.CorePrefsAndroidModule
|
||||
import org.meshtastic.core.prefs.di.CorePrefsModule
|
||||
import org.meshtastic.core.service.di.CoreServiceAndroidModule
|
||||
import org.meshtastic.core.service.di.CoreServiceModule
|
||||
import org.meshtastic.core.ui.di.CoreUiModule
|
||||
import org.meshtastic.feature.connections.di.FeatureConnectionsModule
|
||||
import org.meshtastic.feature.connections.repository.ProbeTableProvider
|
||||
import org.meshtastic.feature.firmware.di.FeatureFirmwareModule
|
||||
import org.meshtastic.feature.intro.di.FeatureIntroModule
|
||||
import org.meshtastic.feature.map.di.FeatureMapModule
|
||||
@@ -72,6 +73,7 @@ import org.meshtastic.feature.settings.di.FeatureSettingsModule
|
||||
CoreServiceModule::class,
|
||||
CoreServiceAndroidModule::class,
|
||||
CoreNetworkModule::class,
|
||||
CoreNetworkAndroidModule::class,
|
||||
CoreUiModule::class,
|
||||
FeatureNodeModule::class,
|
||||
FeatureMessagingModule::class,
|
||||
|
||||
@@ -22,6 +22,7 @@ import androidx.work.WorkManager
|
||||
import androidx.work.workDataOf
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.repository.MessageQueue
|
||||
import org.meshtastic.core.service.worker.SendMessageWorker
|
||||
|
||||
/** Android implementation of [MessageQueue] that uses [WorkManager] for reliable background transmission. */
|
||||
@Single
|
||||
|
||||
@@ -74,7 +74,6 @@ import org.meshtastic.app.navigation.firmwareGraph
|
||||
import org.meshtastic.app.navigation.mapGraph
|
||||
import org.meshtastic.app.navigation.nodesGraph
|
||||
import org.meshtastic.app.navigation.settingsGraph
|
||||
import org.meshtastic.app.service.MeshService
|
||||
import org.meshtastic.core.model.ConnectionState
|
||||
import org.meshtastic.core.model.DeviceType
|
||||
import org.meshtastic.core.model.DeviceVersion
|
||||
@@ -96,6 +95,7 @@ import org.meshtastic.core.resources.should_update
|
||||
import org.meshtastic.core.resources.should_update_firmware
|
||||
import org.meshtastic.core.resources.traceroute
|
||||
import org.meshtastic.core.resources.view_on_map
|
||||
import org.meshtastic.core.service.MeshService
|
||||
import org.meshtastic.core.ui.component.MeshtasticDialog
|
||||
import org.meshtastic.core.ui.component.ScrollToTopEvent
|
||||
import org.meshtastic.core.ui.navigation.icon
|
||||
|
||||
5
conductor/archive/extract_services_20260317/index.md
Normal file
5
conductor/archive/extract_services_20260317/index.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Track extract_services_20260317 Context
|
||||
|
||||
- [Specification](./spec.md)
|
||||
- [Implementation Plan](./plan.md)
|
||||
- [Metadata](./metadata.json)
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"track_id": "extract_services_20260317",
|
||||
"type": "refactor",
|
||||
"status": "new",
|
||||
"created_at": "2026-03-17T00:00:00Z",
|
||||
"updated_at": "2026-03-17T00:00:00Z",
|
||||
"description": "Extract service/worker/radio files from `app` to `core:service/androidMain` and `core:network/androidMain`"
|
||||
}
|
||||
44
conductor/archive/extract_services_20260317/plan.md
Normal file
44
conductor/archive/extract_services_20260317/plan.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Implementation Plan: Extract service/worker/radio files from `app`
|
||||
|
||||
## Phase 1: Preparation & Analysis [checkpoint: 72022ed]
|
||||
- [x] Task: Identify all Android-specific classes to be moved (Services, WorkManager workers, Radio connections in `app`) [fd916e3]
|
||||
- [ ] Locate `Service` classes in `app/src/main/java/org/meshtastic/app`
|
||||
- [ ] Locate WorkManager `Worker` classes
|
||||
- [ ] Locate Radio connection classes
|
||||
- [x] Task: Conductor - User Manual Verification 'Preparation & Analysis' (Protocol in workflow.md)
|
||||
|
||||
## Phase 2: Extraction to `core:service` [checkpoint: ff47af8]
|
||||
- [x] Task: Setup `core:service` module for Android and Common targets (if not already fully configured) [a114084]
|
||||
- [x] Task: Move Android `Service` implementations to `core:service/androidMain` [965def0]
|
||||
- [x] Move the files
|
||||
- [x] Update imports and Koin injections
|
||||
- [x] Task: Abstract shared service logic into `core:service/commonMain` [a85e282]
|
||||
- [x] Write failing tests for abstracted shared logic (TDD Red)
|
||||
- [x] Extract interfaces and platform-agnostic logic (TDD Green)
|
||||
- [x] Refactor the implementations to use these shared abstractions
|
||||
- [x] Task: Conductor - User Manual Verification 'Extraction to core:service' (Protocol in workflow.md)
|
||||
|
||||
## Phase 3: Extraction to `core:network` [checkpoint: 97a5b62]
|
||||
- [x] Task: Move Radio connection and networking files from `app` to `core:network/androidMain` [b5233cf]
|
||||
- [x] Move the files
|
||||
- [x] Update imports and Koin injections
|
||||
- [x] Task: Abstract shared radio/network logic into `core:network/commonMain` [cc1581d]
|
||||
- [x] Write failing tests for abstracted radio logic (TDD Red)
|
||||
- [x] Extract platform-agnostic business logic (TDD Green)
|
||||
- [x] Refactor implementations to use shared abstractions
|
||||
- [x] Task: Conductor - User Manual Verification 'Extraction to core:network' (Protocol in workflow.md)
|
||||
|
||||
## Phase 4: Desktop Integration [checkpoint: fffcedc]
|
||||
- [x] Task: Integrate newly extracted shared abstractions into the `desktop` module [f39df2f]
|
||||
- [x] Implement desktop-specific actuals or Koin bindings for the shared interfaces
|
||||
- [x] Wire up abstracted services/radio logic in desktop Koin graph
|
||||
- [x] Task: Conductor - User Manual Verification 'Desktop Integration' (Protocol in workflow.md)
|
||||
|
||||
## Phase 5: Verification & Cleanup [checkpoint: a0866e0]
|
||||
- [x] Task: Build project and verify no regressions in background processing or radio connectivity [a9edc2e]
|
||||
- [x] Task: Verify test coverage (>80%) for all extracted and refactored code [9cff9bc]
|
||||
- [x] Task: Remove any lingering unused dependencies or dead code in `app` [e39d2e2]
|
||||
- [x] Task: Conductor - User Manual Verification 'Verification & Cleanup' (Protocol in workflow.md)
|
||||
|
||||
## Phase: Review Fixes
|
||||
- [x] Task: Apply review suggestions [1ae9fb6]
|
||||
32
conductor/archive/extract_services_20260317/spec.md
Normal file
32
conductor/archive/extract_services_20260317/spec.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Specification: Extract service/worker/radio files from `app`
|
||||
|
||||
## Overview
|
||||
This track aims to decouple the main `app` module by extracting Android-specific service, WorkManager worker, and radio connection files into `core:service` and `core:network` modules. The goal is to maximize code reuse across Kotlin Multiplatform (KMP) targets, clarify class responsibilities, and improve unit testability by isolating the network and service layers.
|
||||
|
||||
## Goals
|
||||
- **Decouple `app`:** Remove Android-specific service dependencies from the main app module.
|
||||
- **KMP Preparation:** Migrate as much logic as possible into `commonMain` for reuse across platforms.
|
||||
- **Desktop Integration:** If logic is successfully abstracted into `commonMain`, integrate and use it within the `desktop` target to ensure reusability.
|
||||
- **Testability:** Isolate service and network layers to facilitate better unit testing.
|
||||
- **Simplification:** Refactor logic during the move to clarify and simplify responsibilities.
|
||||
|
||||
## Functional Requirements
|
||||
- Identify all service, worker, and radio-related classes currently residing in the `app` module.
|
||||
- Move Android-specific implementations (e.g., `Service`, `Worker`) to `core:service/androidMain` and `core:network/androidMain`.
|
||||
- Extract platform-agnostic business logic and interfaces into `commonMain` within those core modules.
|
||||
- Refactor existing logic where necessary to establish a clear delineation of responsibility.
|
||||
- Update all dependency injections (Koin modules) and imports across the project to reflect the new locations.
|
||||
- Attempt to wire up the newly abstracted shared logic within the `desktop` module if applicable.
|
||||
|
||||
## Non-Functional Requirements
|
||||
- **Architecture Compliance:** Changes must adhere to the MVI / Unidirectional Data Flow and KMP structures defined in `tech-stack.md`.
|
||||
- **Performance:** Refactoring should not negatively impact app startup time or background processing efficiency.
|
||||
- **Code Coverage:** Maintain or improve overall test coverage for the extracted components (>80% target).
|
||||
|
||||
## Acceptance Criteria
|
||||
- [ ] No service, worker, or radio connection classes remain in the `app` module.
|
||||
- [ ] Extracted Android-specific classes compile successfully in `core:service/androidMain` and `core:network/androidMain`.
|
||||
- [ ] Shared business logic compiles successfully in `core:service/commonMain` and `core:network/commonMain`.
|
||||
- [ ] If logic is abstracted for reuse, it is integrated and utilized in the `desktop` target where applicable.
|
||||
- [ ] The app compiles, installs, and runs without regressions in background processing or radio connectivity.
|
||||
- [ ] Unit tests for the moved and refactored classes pass.
|
||||
@@ -20,6 +20,6 @@ Meshtastic-Android is a Kotlin Multiplatform (KMP) application designed to facil
|
||||
- Device configuration and firmware updates
|
||||
|
||||
## Key Architecture Goals
|
||||
- Provide a robust, shared KMP core (`core:model`, `core:ble`, `core:repository`, `core:domain`, `core:data`, `core:network`) to support multiple platforms (Android, Desktop, iOS)
|
||||
- Provide a robust, shared KMP core (`core:model`, `core:ble`, `core:repository`, `core:domain`, `core:data`, `core:network`, `core:service`) to support multiple platforms (Android, Desktop, iOS)
|
||||
- Ensure offline-first functionality and resilient data persistence (Room KMP)
|
||||
- Decouple UI logic into shared components (`core:ui`, `feature:*`) using Compose Multiplatform
|
||||
@@ -7,6 +7,9 @@
|
||||
- **Compose Multiplatform:** Shared UI layer for rendering on Android and Desktop.
|
||||
- **Jetpack Compose:** Used where platform-specific UI (like charts or permissions) is necessary on Android.
|
||||
|
||||
## Background & Services
|
||||
- **Platform Services:** Core service orchestrations and background work are abstracted into `core:service` to maximize logic reuse across targets, using platform-specific implementations (e.g., WorkManager/Service on Android) only where necessary.
|
||||
|
||||
## Architecture
|
||||
- **MVI / Unidirectional Data Flow:** Shared view models using the multiplatform `androidx.lifecycle.ViewModel`.
|
||||
- **JetBrains Navigation 3:** Multiplatform fork for state-based, compose-first navigation without relying on `NavController`.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Project Tracks
|
||||
|
||||
This file tracks all major tracks for the project. Each track has its own detailed plan in its respective folder.
|
||||
|
||||
---
|
||||
|
||||
@@ -258,7 +258,7 @@ class CommandSenderImpl(
|
||||
wantAck = true,
|
||||
id = requestId,
|
||||
channel = nodeManager.nodeDBbyNodeNum[destNum]?.channel ?: 0,
|
||||
decoded = Data(portnum = PortNum.TRACEROUTE_APP, want_response = true),
|
||||
decoded = Data(portnum = PortNum.TRACEROUTE_APP, want_response = true, dest = destNum),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -296,7 +296,7 @@ class CommandSenderImpl(
|
||||
to = destNum,
|
||||
id = requestId,
|
||||
channel = nodeManager.nodeDBbyNodeNum[destNum]?.channel ?: 0,
|
||||
decoded = Data(portnum = portNum, payload = payloadBytes, want_response = true),
|
||||
decoded = Data(portnum = portNum, payload = payloadBytes, want_response = true, dest = destNum),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -349,7 +349,7 @@ class CommandSenderImpl(
|
||||
wantAck = true,
|
||||
id = requestId,
|
||||
channel = nodeManager.nodeDBbyNodeNum[destNum]?.channel ?: 0,
|
||||
decoded = Data(portnum = PortNum.NEIGHBORINFO_APP, want_response = true),
|
||||
decoded = Data(portnum = PortNum.NEIGHBORINFO_APP, want_response = true, dest = destNum),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -52,4 +52,9 @@ data class DeviceVersion(val asString: String) : Comparable<DeviceVersion> {
|
||||
}
|
||||
|
||||
override fun compareTo(other: DeviceVersion): Int = asInt.compareTo(other.asInt)
|
||||
|
||||
companion object {
|
||||
const val MIN_FW_VERSION = "2.5.14"
|
||||
const val ABS_MIN_FW_VERSION = "2.3.15"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,10 @@ kotlin {
|
||||
val jvmMain by getting { dependencies { implementation(libs.ktor.client.java) } }
|
||||
|
||||
androidMain.dependencies {
|
||||
implementation(projects.core.ble)
|
||||
implementation(projects.core.prefs)
|
||||
implementation(libs.org.eclipse.paho.client.mqttv3)
|
||||
implementation(libs.usb.serial.android)
|
||||
implementation(libs.coil.network.okhttp)
|
||||
implementation(libs.coil.svg)
|
||||
implementation(libs.ktor.client.okhttp)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import android.app.Application
|
||||
import android.provider.Settings
|
||||
@@ -37,8 +37,8 @@ import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.annotation.Named
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.app.BuildConfig
|
||||
import org.meshtastic.core.ble.BluetoothRepository
|
||||
import org.meshtastic.core.common.BuildConfigProvider
|
||||
import org.meshtastic.core.common.util.BinaryLogFile
|
||||
import org.meshtastic.core.common.util.handledLaunch
|
||||
import org.meshtastic.core.common.util.ignoreException
|
||||
@@ -49,11 +49,11 @@ import org.meshtastic.core.model.ConnectionState
|
||||
import org.meshtastic.core.model.InterfaceId
|
||||
import org.meshtastic.core.model.MeshActivity
|
||||
import org.meshtastic.core.model.util.anonymize
|
||||
import org.meshtastic.core.network.repository.NetworkRepository
|
||||
import org.meshtastic.core.repository.PlatformAnalytics
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.core.repository.RadioPrefs
|
||||
import org.meshtastic.core.repository.RadioTransport
|
||||
import org.meshtastic.feature.connections.repository.NetworkRepository
|
||||
import org.meshtastic.proto.Heartbeat
|
||||
import org.meshtastic.proto.ToRadio
|
||||
|
||||
@@ -73,6 +73,7 @@ class AndroidRadioInterfaceService(
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val bluetoothRepository: BluetoothRepository,
|
||||
private val networkRepository: NetworkRepository,
|
||||
private val buildConfigProvider: BuildConfigProvider,
|
||||
@Named("ProcessLifecycle") private val processLifecycle: Lifecycle,
|
||||
private val radioPrefs: RadioPrefs,
|
||||
private val interfaceFactory: Lazy<InterfaceFactory>,
|
||||
@@ -187,7 +188,7 @@ class AndroidRadioInterfaceService(
|
||||
interfaceFactory.value.toInterfaceAddress(interfaceId, rest)
|
||||
|
||||
override fun isMockInterface(): Boolean =
|
||||
BuildConfig.DEBUG || Settings.System.getString(context.contentResolver, "firebase.test.lab") == "true"
|
||||
buildConfigProvider.isDebug || Settings.System.getString(context.contentResolver, "firebase.test.lab") == "true"
|
||||
|
||||
override fun getDeviceAddress(): String? {
|
||||
// If the user has unpaired our device, treat things as if we don't have one
|
||||
@@ -14,7 +14,9 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
@file:Suppress("TooManyFunctions", "TooGenericExceptionCaught")
|
||||
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import co.touchlab.kermit.Logger
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.ble.BleConnectionFactory
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.model.InterfaceId
|
||||
@@ -14,14 +14,14 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import org.meshtastic.core.common.util.nowMillis
|
||||
import org.meshtastic.core.network.repository.SerialConnection
|
||||
import org.meshtastic.core.network.repository.SerialConnectionListener
|
||||
import org.meshtastic.core.network.repository.UsbRepository
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.feature.connections.repository.SerialConnection
|
||||
import org.meshtastic.feature.connections.repository.SerialConnectionListener
|
||||
import org.meshtastic.feature.connections.repository.UsbRepository
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
/** An interface that assumes we are talking to a meshtastic device via USB serial */
|
||||
@@ -14,11 +14,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.network.repository.UsbRepository
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.feature.connections.repository.UsbRepository
|
||||
|
||||
/** Factory for creating `SerialInterface` instances. */
|
||||
@Single
|
||||
@@ -14,13 +14,13 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import android.hardware.usb.UsbManager
|
||||
import com.hoho.android.usbserial.driver.UsbSerialDriver
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.network.repository.UsbRepository
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.feature.connections.repository.UsbRepository
|
||||
|
||||
/** Serial/USB interface backend implementation. */
|
||||
@Single
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import org.meshtastic.core.common.util.handledLaunch
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.repository
|
||||
package org.meshtastic.core.network.repository
|
||||
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.repository
|
||||
package org.meshtastic.core.network.repository
|
||||
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.nsd.NsdManager
|
||||
@@ -14,7 +14,9 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.repository
|
||||
@file:Suppress("SwallowedException")
|
||||
|
||||
package org.meshtastic.core.network.repository
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.net.nsd.NsdManager
|
||||
@@ -14,7 +14,9 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.repository
|
||||
@file:Suppress("MagicNumber")
|
||||
|
||||
package org.meshtastic.core.network.repository
|
||||
|
||||
import com.hoho.android.usbserial.driver.CdcAcmSerialDriver
|
||||
import com.hoho.android.usbserial.driver.ProbeTable
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.repository
|
||||
package org.meshtastic.core.network.repository
|
||||
|
||||
/** USB serial connection. */
|
||||
interface SerialConnection : AutoCloseable {
|
||||
@@ -14,7 +14,9 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.repository
|
||||
@file:Suppress("MagicNumber")
|
||||
|
||||
package org.meshtastic.core.network.repository
|
||||
|
||||
import android.hardware.usb.UsbManager
|
||||
import co.touchlab.kermit.Logger
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.repository
|
||||
package org.meshtastic.core.network.repository
|
||||
|
||||
/** Callbacks indicating state changes in the USB serial connection. */
|
||||
interface SerialConnectionListener {
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.repository
|
||||
package org.meshtastic.core.network.repository
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.repository
|
||||
package org.meshtastic.core.network.repository
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.repository
|
||||
package org.meshtastic.core.network.repository
|
||||
|
||||
import android.app.Application
|
||||
import android.hardware.usb.UsbDevice
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.meshtastic.core.repository.RadioTransport
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.core.repository.RadioTransport
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.meshtastic.core.repository.RadioTransport
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.repository.radio
|
||||
package org.meshtastic.core.network.radio
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.repository
|
||||
package org.meshtastic.core.network.repository
|
||||
|
||||
object NetworkConstants {
|
||||
const val SERVICE_PORT = 4403
|
||||
@@ -36,6 +36,7 @@ kotlin {
|
||||
implementation(projects.core.data)
|
||||
implementation(projects.core.database)
|
||||
implementation(projects.core.model)
|
||||
implementation(projects.core.navigation)
|
||||
implementation(projects.core.prefs)
|
||||
implementation(projects.core.proto)
|
||||
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.service
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.workDataOf
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.app.messaging.domain.worker.SendMessageWorker
|
||||
import org.meshtastic.core.repository.MeshWorkerManager
|
||||
import org.meshtastic.core.service.worker.SendMessageWorker
|
||||
|
||||
@Single
|
||||
class AndroidMeshWorkerManager(private val workManager: WorkManager) : MeshWorkerManager {
|
||||
@@ -200,7 +200,7 @@ class AndroidRadioControllerImpl(
|
||||
// Ensure service is running/restarted to handle the new address
|
||||
val intent =
|
||||
android.content.Intent().apply {
|
||||
setClassName("com.geeksville.mesh", "org.meshtastic.app.service.MeshService")
|
||||
setClassName("com.geeksville.mesh", "org.meshtastic.core.service.MeshService")
|
||||
}
|
||||
context.startForegroundService(intent)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.service
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.service
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import org.meshtastic.core.api.MeshtasticIntent
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.service
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.service
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
@@ -27,12 +27,8 @@ import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.meshtastic.app.BuildConfig
|
||||
import org.meshtastic.core.common.hasLocationPermission
|
||||
import org.meshtastic.core.common.util.handledLaunch
|
||||
import org.meshtastic.core.common.util.toRemoteExceptions
|
||||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.model.DeviceVersion
|
||||
@@ -44,17 +40,12 @@ import org.meshtastic.core.model.RadioNotConnectedException
|
||||
import org.meshtastic.core.repository.CommandSender
|
||||
import org.meshtastic.core.repository.MeshConnectionManager
|
||||
import org.meshtastic.core.repository.MeshLocationManager
|
||||
import org.meshtastic.core.repository.MeshMessageProcessor
|
||||
import org.meshtastic.core.repository.MeshRouter
|
||||
import org.meshtastic.core.repository.MeshServiceNotifications
|
||||
import org.meshtastic.core.repository.NodeManager
|
||||
import org.meshtastic.core.repository.PacketHandler
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.core.repository.SERVICE_NOTIFY_ID
|
||||
import org.meshtastic.core.repository.ServiceBroadcasts
|
||||
import org.meshtastic.core.repository.ServiceRepository
|
||||
import org.meshtastic.core.service.IMeshService
|
||||
import org.meshtastic.feature.connections.NO_DEVICE_SELECTED
|
||||
import org.meshtastic.proto.PortNum
|
||||
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
@@ -64,21 +55,17 @@ class MeshService : Service() {
|
||||
|
||||
private val serviceRepository: ServiceRepository by inject()
|
||||
|
||||
private val packetHandler: PacketHandler by inject()
|
||||
|
||||
private val serviceBroadcasts: ServiceBroadcasts by inject()
|
||||
|
||||
private val nodeManager: NodeManager by inject()
|
||||
|
||||
private val messageProcessor: MeshMessageProcessor by inject()
|
||||
|
||||
private val commandSender: CommandSender by inject()
|
||||
|
||||
private val locationManager: MeshLocationManager by inject()
|
||||
|
||||
private val connectionManager: MeshConnectionManager by inject()
|
||||
|
||||
private val serviceNotifications: MeshServiceNotifications by inject()
|
||||
private val orchestrator: MeshServiceOrchestrator by inject()
|
||||
|
||||
private val router: MeshRouter by inject()
|
||||
|
||||
@@ -102,8 +89,8 @@ class MeshService : Service() {
|
||||
startService(context)
|
||||
}
|
||||
|
||||
val minDeviceVersion = DeviceVersion(BuildConfig.MIN_FW_VERSION)
|
||||
val absoluteMinDeviceVersion = DeviceVersion(BuildConfig.ABS_MIN_FW_VERSION)
|
||||
val minDeviceVersion = DeviceVersion(DeviceVersion.MIN_FW_VERSION)
|
||||
val absoluteMinDeviceVersion = DeviceVersion(DeviceVersion.ABS_MIN_FW_VERSION)
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
@@ -121,29 +108,13 @@ class MeshService : Service() {
|
||||
throw e
|
||||
}
|
||||
Logger.i { "Creating mesh service" }
|
||||
serviceNotifications.initChannels()
|
||||
|
||||
packetHandler.start(serviceScope)
|
||||
router.start(serviceScope)
|
||||
nodeManager.start(serviceScope)
|
||||
connectionManager.start(serviceScope)
|
||||
messageProcessor.start(serviceScope)
|
||||
commandSender.start(serviceScope)
|
||||
|
||||
serviceScope.handledLaunch { radioInterfaceService.connect() }
|
||||
|
||||
radioInterfaceService.receivedData
|
||||
.onEach { bytes -> messageProcessor.handleFromRadio(bytes, nodeManager.myNodeNum) }
|
||||
.launchIn(serviceScope)
|
||||
|
||||
serviceRepository.serviceAction.onEach(router.actionHandler::onServiceAction).launchIn(serviceScope)
|
||||
|
||||
nodeManager.loadCachedNodeDB()
|
||||
orchestrator.start()
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val a = radioInterfaceService.getDeviceAddress()
|
||||
val wantForeground = a != null && a != NO_DEVICE_SELECTED
|
||||
val wantForeground = a != null && a != "n"
|
||||
|
||||
val notification = connectionManager.updateStatusNotification() as android.app.Notification
|
||||
|
||||
@@ -207,6 +178,7 @@ class MeshService : Service() {
|
||||
override fun onDestroy() {
|
||||
Logger.i { "Destroying mesh service" }
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
orchestrator.stop()
|
||||
serviceJob.cancel()
|
||||
super.onDestroy()
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.service
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
@@ -40,11 +40,6 @@ import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.app.MainActivity
|
||||
import org.meshtastic.app.R.raw
|
||||
import org.meshtastic.app.service.MarkAsReadReceiver.Companion.MARK_AS_READ_ACTION
|
||||
import org.meshtastic.app.service.ReactionReceiver.Companion.REACT_ACTION
|
||||
import org.meshtastic.app.service.ReplyReceiver.Companion.KEY_TEXT_REPLY
|
||||
import org.meshtastic.core.common.util.nowMillis
|
||||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.model.Message
|
||||
@@ -55,6 +50,7 @@ import org.meshtastic.core.repository.MeshServiceNotifications
|
||||
import org.meshtastic.core.repository.NodeRepository
|
||||
import org.meshtastic.core.repository.PacketRepository
|
||||
import org.meshtastic.core.repository.SERVICE_NOTIFY_ID
|
||||
import org.meshtastic.core.resources.R.raw
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.client_notification
|
||||
import org.meshtastic.core.resources.getString
|
||||
@@ -87,6 +83,9 @@ import org.meshtastic.core.resources.no_local_stats
|
||||
import org.meshtastic.core.resources.powered
|
||||
import org.meshtastic.core.resources.reply
|
||||
import org.meshtastic.core.resources.you
|
||||
import org.meshtastic.core.service.MarkAsReadReceiver.Companion.MARK_AS_READ_ACTION
|
||||
import org.meshtastic.core.service.ReactionReceiver.Companion.REACT_ACTION
|
||||
import org.meshtastic.core.service.ReplyReceiver.Companion.KEY_TEXT_REPLY
|
||||
import org.meshtastic.proto.ClientNotification
|
||||
import org.meshtastic.proto.DeviceMetrics
|
||||
import org.meshtastic.proto.LocalStats
|
||||
@@ -453,7 +452,7 @@ class MeshServiceNotificationsImpl(
|
||||
|
||||
val summaryNotification =
|
||||
commonBuilder(NotificationType.DirectMessage)
|
||||
.setSmallIcon(org.meshtastic.app.R.drawable.app_icon)
|
||||
.setSmallIcon(context.applicationInfo.icon)
|
||||
.setStyle(messagingStyle)
|
||||
.setGroup(GROUP_KEY_MESSAGES)
|
||||
.setGroupSummary(true)
|
||||
@@ -697,14 +696,17 @@ class MeshServiceNotificationsImpl(
|
||||
|
||||
// region Helper/Builder Methods
|
||||
private val openAppIntent: PendingIntent by lazy {
|
||||
val intent = Intent(context, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_SINGLE_TOP }
|
||||
val intent =
|
||||
Intent(context, Class.forName("org.meshtastic.app.MainActivity")).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
}
|
||||
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
private fun createOpenMessageIntent(contactKey: String): PendingIntent {
|
||||
val deepLinkUri = "$DEEP_LINK_BASE_URI/messages/$contactKey".toUri()
|
||||
val deepLinkIntent =
|
||||
Intent(Intent.ACTION_VIEW, deepLinkUri, context, MainActivity::class.java).apply {
|
||||
Intent(Intent.ACTION_VIEW, deepLinkUri, context, Class.forName("org.meshtastic.app.MainActivity")).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
}
|
||||
|
||||
@@ -717,7 +719,7 @@ class MeshServiceNotificationsImpl(
|
||||
private fun createOpenWaypointIntent(waypointId: Int): PendingIntent {
|
||||
val deepLinkUri = "$DEEP_LINK_BASE_URI/map?waypointId=$waypointId".toUri()
|
||||
val deepLinkIntent =
|
||||
Intent(Intent.ACTION_VIEW, deepLinkUri, context, MainActivity::class.java).apply {
|
||||
Intent(Intent.ACTION_VIEW, deepLinkUri, context, Class.forName("org.meshtastic.app.MainActivity")).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
}
|
||||
|
||||
@@ -730,7 +732,7 @@ class MeshServiceNotificationsImpl(
|
||||
private fun createOpenNodeDetailIntent(nodeNum: Int): PendingIntent {
|
||||
val deepLinkUri = "$DEEP_LINK_BASE_URI/node?destNum=$nodeNum".toUri()
|
||||
val deepLinkIntent =
|
||||
Intent(Intent.ACTION_VIEW, deepLinkUri, context, MainActivity::class.java).apply {
|
||||
Intent(Intent.ACTION_VIEW, deepLinkUri, context, Class.forName("org.meshtastic.app.MainActivity")).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
}
|
||||
|
||||
@@ -811,7 +813,7 @@ class MeshServiceNotificationsImpl(
|
||||
type: NotificationType,
|
||||
contentIntent: PendingIntent? = null,
|
||||
): NotificationCompat.Builder {
|
||||
val smallIcon = org.meshtastic.app.R.drawable.app_icon
|
||||
val smallIcon = context.applicationInfo.icon
|
||||
|
||||
return NotificationCompat.Builder(context, type.channelId)
|
||||
.setSmallIcon(smallIcon)
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.service
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import android.app.ForegroundServiceStartNotAllowedException
|
||||
import android.content.Context
|
||||
@@ -23,8 +23,7 @@ import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.OutOfQuotaPolicy
|
||||
import androidx.work.WorkManager
|
||||
import co.touchlab.kermit.Logger
|
||||
import org.meshtastic.app.BuildConfig
|
||||
import org.meshtastic.app.worker.ServiceKeepAliveWorker
|
||||
import org.meshtastic.core.service.worker.ServiceKeepAliveWorker
|
||||
|
||||
// / Helper function to start running our service
|
||||
fun MeshService.Companion.startService(context: Context) {
|
||||
@@ -36,7 +35,7 @@ fun MeshService.Companion.startService(context: Context) {
|
||||
// Before binding we want to explicitly create - so the service stays alive forever (so it can keep
|
||||
// listening for the bluetooth packets arriving from the radio. And when they arrive forward them
|
||||
// to Signal or whatever.
|
||||
Logger.i { "Trying to start service debug=${BuildConfig.DEBUG}" }
|
||||
Logger.i { "Trying to start service debug=${false}" }
|
||||
|
||||
val intent = createIntent(context)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.service
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.service
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.service
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.worker
|
||||
package org.meshtastic.core.service.worker
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.CoroutineWorker
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.messaging.domain.worker
|
||||
package org.meshtastic.core.service.worker
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.CoroutineWorker
|
||||
@@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.worker
|
||||
package org.meshtastic.core.service.worker
|
||||
|
||||
import android.app.Notification
|
||||
import android.content.Context
|
||||
@@ -26,11 +26,10 @@ import androidx.work.ForegroundInfo
|
||||
import androidx.work.WorkerParameters
|
||||
import co.touchlab.kermit.Logger
|
||||
import org.koin.android.annotation.KoinWorker
|
||||
import org.meshtastic.app.R
|
||||
import org.meshtastic.app.service.MeshService
|
||||
import org.meshtastic.app.service.startService
|
||||
import org.meshtastic.core.repository.MeshServiceNotifications
|
||||
import org.meshtastic.core.repository.SERVICE_NOTIFY_ID
|
||||
import org.meshtastic.core.service.MeshService
|
||||
import org.meshtastic.core.service.startService
|
||||
|
||||
/**
|
||||
* A worker whose sole purpose is to start the MeshService from the background. This is used as a fallback when
|
||||
@@ -81,7 +80,7 @@ class ServiceKeepAliveWorker(
|
||||
// We use "my_service" which matches NotificationType.ServiceState.channelId in MeshServiceNotificationsImpl
|
||||
|
||||
return NotificationCompat.Builder(applicationContext, "my_service")
|
||||
.setSmallIcon(R.drawable.ic_launcher_foreground)
|
||||
.setSmallIcon(applicationContext.applicationInfo.icon)
|
||||
.setContentTitle("Resuming Mesh Service")
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setOngoing(true)
|
||||
@@ -22,6 +22,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.common.util.handledLaunch
|
||||
import org.meshtastic.core.repository.CommandSender
|
||||
import org.meshtastic.core.repository.MeshConnectionManager
|
||||
@@ -42,6 +43,7 @@ import org.meshtastic.core.repository.ServiceRepository
|
||||
* All injected dependencies are `commonMain` interfaces with real implementations in `core:data`.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@Single
|
||||
class MeshServiceOrchestrator(
|
||||
private val radioInterfaceService: RadioInterfaceService,
|
||||
private val serviceRepository: ServiceRepository,
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import org.meshtastic.core.repository.CommandSender
|
||||
import org.meshtastic.core.repository.MeshConnectionManager
|
||||
import org.meshtastic.core.repository.MeshMessageProcessor
|
||||
import org.meshtastic.core.repository.MeshRouter
|
||||
import org.meshtastic.core.repository.MeshServiceNotifications
|
||||
import org.meshtastic.core.repository.NodeManager
|
||||
import org.meshtastic.core.repository.PacketHandler
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.core.repository.ServiceRepository
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class MeshServiceOrchestratorTest {
|
||||
|
||||
@Test
|
||||
fun testStartWiresComponents() {
|
||||
val radioInterfaceService = mockk<RadioInterfaceService>(relaxed = true)
|
||||
val serviceRepository = mockk<ServiceRepository>(relaxed = true)
|
||||
val packetHandler = mockk<PacketHandler>(relaxed = true)
|
||||
val nodeManager = mockk<NodeManager>(relaxed = true)
|
||||
val messageProcessor = mockk<MeshMessageProcessor>(relaxed = true)
|
||||
val commandSender = mockk<CommandSender>(relaxed = true)
|
||||
val connectionManager = mockk<MeshConnectionManager>(relaxed = true)
|
||||
val router = mockk<MeshRouter>(relaxed = true)
|
||||
val serviceNotifications = mockk<MeshServiceNotifications>(relaxed = true)
|
||||
|
||||
every { radioInterfaceService.receivedData } returns MutableSharedFlow()
|
||||
every { serviceRepository.serviceAction } returns MutableSharedFlow()
|
||||
|
||||
val orchestrator =
|
||||
MeshServiceOrchestrator(
|
||||
radioInterfaceService,
|
||||
serviceRepository,
|
||||
packetHandler,
|
||||
nodeManager,
|
||||
messageProcessor,
|
||||
commandSender,
|
||||
connectionManager,
|
||||
router,
|
||||
serviceNotifications,
|
||||
)
|
||||
|
||||
assertFalse(orchestrator.isRunning)
|
||||
orchestrator.start()
|
||||
assertTrue(orchestrator.isRunning)
|
||||
|
||||
verify { serviceNotifications.initChannels() }
|
||||
verify { packetHandler.start(any()) }
|
||||
verify { nodeManager.loadCachedNodeDB() }
|
||||
|
||||
orchestrator.stop()
|
||||
assertFalse(orchestrator.isRunning)
|
||||
}
|
||||
}
|
||||
@@ -49,11 +49,11 @@ import org.koin.core.context.startKoin
|
||||
import org.meshtastic.core.datastore.UiPreferencesDataSource
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.TopLevelDestination
|
||||
import org.meshtastic.core.service.MeshServiceOrchestrator
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.desktop.data.DesktopPreferencesDataSource
|
||||
import org.meshtastic.desktop.di.desktopModule
|
||||
import org.meshtastic.desktop.di.desktopPlatformModule
|
||||
import org.meshtastic.desktop.radio.DesktopMeshServiceController
|
||||
import org.meshtastic.desktop.ui.DesktopMainScreen
|
||||
import org.meshtastic.desktop.ui.navSavedStateConfig
|
||||
import java.util.Locale
|
||||
@@ -82,7 +82,7 @@ fun main() = application(exitProcessOnExit = false) {
|
||||
val systemLocale = remember { Locale.getDefault() }
|
||||
|
||||
// Start the mesh service processing chain (desktop equivalent of Android's MeshService)
|
||||
val meshServiceController = remember { koinApp.koin.get<DesktopMeshServiceController>() }
|
||||
val meshServiceController = remember { koinApp.koin.get<MeshServiceOrchestrator>() }
|
||||
DisposableEffect(Unit) {
|
||||
meshServiceController.start()
|
||||
onDispose { meshServiceController.stop() }
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.meshtastic.core.repository.PlatformAnalytics
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.core.repository.ServiceBroadcasts
|
||||
import org.meshtastic.core.repository.ServiceRepository
|
||||
import org.meshtastic.desktop.radio.DesktopMeshServiceController
|
||||
import org.meshtastic.desktop.radio.DesktopRadioInterfaceService
|
||||
import org.meshtastic.desktop.stub.NoopAppWidgetUpdater
|
||||
import org.meshtastic.desktop.stub.NoopCompassHeadingProvider
|
||||
@@ -151,19 +150,6 @@ private fun desktopPlatformStubsModule() = module {
|
||||
single<org.meshtastic.feature.node.compass.MagneticFieldProvider> { NoopMagneticFieldProvider() }
|
||||
|
||||
// Desktop mesh service controller — replaces Android's MeshService lifecycle
|
||||
single {
|
||||
DesktopMeshServiceController(
|
||||
radioInterfaceService = get(),
|
||||
serviceRepository = get(),
|
||||
messageProcessor = get(),
|
||||
connectionManager = get(),
|
||||
packetHandler = get(),
|
||||
router = get(),
|
||||
nodeManager = get(),
|
||||
commandSender = get(),
|
||||
)
|
||||
}
|
||||
|
||||
// Ktor HttpClient for JVM/Desktop (equivalent of CoreNetworkAndroidModule on Android)
|
||||
single<HttpClient> { HttpClient(Java) { install(ContentNegotiation) { json(get<Json>()) } } }
|
||||
|
||||
|
||||
@@ -1,110 +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/>.
|
||||
*/
|
||||
package org.meshtastic.desktop.radio
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.meshtastic.core.common.util.handledLaunch
|
||||
import org.meshtastic.core.repository.CommandSender
|
||||
import org.meshtastic.core.repository.MeshConnectionManager
|
||||
import org.meshtastic.core.repository.MeshMessageProcessor
|
||||
import org.meshtastic.core.repository.MeshRouter
|
||||
import org.meshtastic.core.repository.NodeManager
|
||||
import org.meshtastic.core.repository.PacketHandler
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.core.repository.ServiceRepository
|
||||
|
||||
/**
|
||||
* Desktop equivalent of Android's `MeshService.onCreate()`.
|
||||
*
|
||||
* Starts the full message-processing chain that connects the radio transport layer to the business logic:
|
||||
* ```
|
||||
* radioInterfaceService.receivedData
|
||||
* → messageProcessor.handleFromRadio(bytes, myNodeNum)
|
||||
* → FromRadioPacketHandler → MeshRouter/PacketHandler/etc.
|
||||
* ```
|
||||
*
|
||||
* On Android this chain runs inside an Android `Service` (foreground service with notifications). On Desktop there is
|
||||
* no Android Service concept, so this controller manages the same lifecycle in-process, started at app launch time.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
class DesktopMeshServiceController(
|
||||
private val radioInterfaceService: RadioInterfaceService,
|
||||
private val serviceRepository: ServiceRepository,
|
||||
private val messageProcessor: MeshMessageProcessor,
|
||||
private val connectionManager: MeshConnectionManager,
|
||||
private val packetHandler: PacketHandler,
|
||||
private val router: MeshRouter,
|
||||
private val nodeManager: NodeManager,
|
||||
private val commandSender: CommandSender,
|
||||
) {
|
||||
private var serviceScope: CoroutineScope? = null
|
||||
|
||||
/**
|
||||
* Starts the mesh service processing chain.
|
||||
*
|
||||
* This should be called once at application startup (after Koin is initialized). It mirrors the initialization
|
||||
* logic from `MeshService.onCreate()`.
|
||||
*/
|
||||
@Suppress("InjectDispatcher")
|
||||
fun start() {
|
||||
if (serviceScope != null) {
|
||||
Logger.w { "DesktopMeshServiceController: Already started, ignoring duplicate start()" }
|
||||
return
|
||||
}
|
||||
|
||||
Logger.i { "DesktopMeshServiceController: Starting mesh service processing chain" }
|
||||
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
serviceScope = scope
|
||||
|
||||
// Start all processing components (same order as MeshService.onCreate)
|
||||
packetHandler.start(scope)
|
||||
router.start(scope)
|
||||
nodeManager.start(scope)
|
||||
connectionManager.start(scope)
|
||||
messageProcessor.start(scope)
|
||||
commandSender.start(scope)
|
||||
|
||||
// Auto-connect to saved device address (mirrors MeshService.onCreate)
|
||||
scope.handledLaunch { radioInterfaceService.connect() }
|
||||
|
||||
// Wire the data flow: radio → message processor
|
||||
radioInterfaceService.receivedData
|
||||
.onEach { bytes -> messageProcessor.handleFromRadio(bytes, nodeManager.myNodeNum) }
|
||||
.launchIn(scope)
|
||||
|
||||
// Wire service actions to the router
|
||||
serviceRepository.serviceAction.onEach(router.actionHandler::onServiceAction).launchIn(scope)
|
||||
|
||||
// Load any cached node database
|
||||
nodeManager.loadCachedNodeDB()
|
||||
|
||||
Logger.i { "DesktopMeshServiceController: Processing chain started" }
|
||||
}
|
||||
|
||||
/** Stops the mesh service processing chain and cancels all coroutines. */
|
||||
fun stop() {
|
||||
Logger.i { "DesktopMeshServiceController: Stopping" }
|
||||
serviceScope?.cancel("DesktopMeshServiceController stopped")
|
||||
serviceScope = null
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ Based on the latest codebase investigation, the following steps are proposed to
|
||||
- Parity tests exist in `core:navigation/commonTest` (`NavigationParityTest`) and `desktop/test` (`DesktopTopLevelDestinationParityTest`).
|
||||
- Remaining parity work is documented in [`decisions/navigation3-parity-2026-03.md`](./decisions/navigation3-parity-2026-03.md): serializer registration validation and platform exception tracking.
|
||||
|
||||
## Remaining App-Only ViewModels
|
||||
## App Module Thinning Status
|
||||
|
||||
All major ViewModels have now been extracted to `commonMain` and no longer rely on Android-specific subclasses. Platform-specific dependencies (like `android.net.Uri` or Location permissions) have been successfully isolated behind injected `core:repository` interfaces (e.g., `FileService`, `LocationService`).
|
||||
|
||||
@@ -133,6 +133,18 @@ Extracted to shared `commonMain` (no longer app-only):
|
||||
- `ChannelViewModel` → `feature:settings/commonMain`
|
||||
- `NodeMapViewModel` → `feature:map/commonMain`
|
||||
|
||||
Extracted to core KMP modules (Android-specific implementations):
|
||||
- Android Services, WorkManager Workers, and BroadcastReceivers → `core:service/androidMain`
|
||||
- BLE, USB/Serial, TCP radio connections, and NsdManager → `core:network/androidMain`
|
||||
|
||||
Remaining to be extracted from `:app` to achieve a true thin-shell module:
|
||||
- Navigation routes (`ChannelsNavigation.kt`, `SettingsNavigation.kt`, etc.)
|
||||
- Android App Widgets (`LocalStatsWidget.kt`, `AndroidAppWidgetUpdater.kt`)
|
||||
- Message Queue implementation (`WorkManagerMessageQueue.kt`)
|
||||
- Location provider bindings (`AndroidMeshLocationManager.kt`)
|
||||
- Top-level UI composition (`ui/Main.kt`, `ui/node/AdaptiveNodeListScreen.kt`)
|
||||
- Root Activity and Koin bootstrapping (`MainActivity.kt`, `MeshUtilApplication.kt`, `MeshServiceClient.kt`)
|
||||
|
||||
## Prerelease Dependencies
|
||||
|
||||
| Dependency | Version | Why |
|
||||
|
||||
@@ -87,7 +87,8 @@ These items address structural gaps identified in the March 2026 architecture re
|
||||
|
||||
1. **App module thinning** — Extracted ChannelViewModel, NodeMapViewModel, NodeContextMenu, EmptyDetailPlaceholder to shared modules.
|
||||
- ✅ **Done:** Extracted remaining 5 ViewModels: `SettingsViewModel`, `RadioConfigViewModel`, `DebugViewModel`, `MetricsViewModel`, `UIViewModel` to shared KMP modules.
|
||||
- **Next:** Extract service/worker/radio files from `app` to `core:service/androidMain` and `core:network/androidMain`.
|
||||
- ✅ **Done:** Extracted service, worker, and radio files from `app` to `core:service/androidMain` and `core:network/androidMain`.
|
||||
- **Next:** Extract remaining Android-specific files (e.g., Navigation files, App Widgets, message queues, and root Activity logic) out of `:app` to establish a truly thin app module.
|
||||
2. **Serial/USB transport** — direct radio connection on Desktop via jSerialComm
|
||||
3. **MQTT transport** — cloud relay operation (KMP, benefits all targets)
|
||||
4. **Evaluate KMP-native mocking** — Evaluate `mockative` or similar to replace `mockk` in `commonMain` of `core:testing` for iOS readiness.
|
||||
|
||||
@@ -50,6 +50,7 @@ kotlin {
|
||||
implementation(projects.core.service)
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.ble)
|
||||
implementation(projects.core.network)
|
||||
implementation(projects.feature.settings)
|
||||
|
||||
implementation(libs.jetbrains.lifecycle.viewmodel.compose)
|
||||
|
||||
@@ -27,12 +27,12 @@ import org.meshtastic.core.ble.BluetoothRepository
|
||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||
import org.meshtastic.core.model.RadioController
|
||||
import org.meshtastic.core.model.util.anonymize
|
||||
import org.meshtastic.core.network.repository.UsbRepository
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.core.repository.ServiceRepository
|
||||
import org.meshtastic.feature.connections.model.AndroidUsbDeviceData
|
||||
import org.meshtastic.feature.connections.model.DeviceListEntry
|
||||
import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
||||
import org.meshtastic.feature.connections.repository.UsbRepository
|
||||
|
||||
@KoinViewModel
|
||||
@Suppress("LongParameterList", "TooManyFunctions")
|
||||
|
||||
@@ -28,6 +28,9 @@ import org.meshtastic.core.common.database.DatabaseManager
|
||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||
import org.meshtastic.core.datastore.model.RecentAddress
|
||||
import org.meshtastic.core.model.Node
|
||||
import org.meshtastic.core.network.repository.NetworkRepository
|
||||
import org.meshtastic.core.network.repository.NetworkRepository.Companion.toAddressString
|
||||
import org.meshtastic.core.network.repository.UsbRepository
|
||||
import org.meshtastic.core.repository.NodeRepository
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.core.resources.Res
|
||||
@@ -38,9 +41,6 @@ import org.meshtastic.feature.connections.model.DeviceListEntry
|
||||
import org.meshtastic.feature.connections.model.DiscoveredDevices
|
||||
import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
||||
import org.meshtastic.feature.connections.model.getMeshtasticShortName
|
||||
import org.meshtastic.feature.connections.repository.NetworkRepository
|
||||
import org.meshtastic.feature.connections.repository.NetworkRepository.Companion.toAddressString
|
||||
import org.meshtastic.feature.connections.repository.UsbRepository
|
||||
import java.util.Locale
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
||||
@@ -50,6 +50,7 @@ import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.common.util.isValidAddress
|
||||
import org.meshtastic.core.model.ConnectionState
|
||||
import org.meshtastic.core.network.repository.NetworkConstants
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.add_network_device
|
||||
import org.meshtastic.core.resources.address
|
||||
@@ -60,7 +61,6 @@ import org.meshtastic.core.resources.no_network_devices_found
|
||||
import org.meshtastic.core.resources.recent_network_devices
|
||||
import org.meshtastic.feature.connections.ScannerViewModel
|
||||
import org.meshtastic.feature.connections.model.DeviceListEntry
|
||||
import org.meshtastic.feature.connections.repository.NetworkConstants
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
||||
@@ -134,7 +134,7 @@ class MainActivity : ComponentActivity() {
|
||||
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.app.service.MeshService")
|
||||
intent.setClassName("com.geeksville.mesh", "org.meshtastic.core.service.MeshService")
|
||||
}
|
||||
|
||||
val success = bindService(intent, serviceConnection, BIND_AUTO_CREATE)
|
||||
|
||||
Reference in New Issue
Block a user