refactor: migrate core modules to Kotlin Multiplatform and consolidat… (#4735)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich
2026-03-06 16:06:50 -06:00
committed by GitHub
parent f3775a601c
commit cffbd08806
265 changed files with 1383 additions and 1340 deletions

View File

@@ -44,7 +44,7 @@ if (keystorePropertiesFile.exists()) {
}
configure<ApplicationExtension> {
namespace = configProperties.getProperty("APPLICATION_ID")
namespace = "org.meshtastic.app"
signingConfigs {
create("release") {
@@ -150,7 +150,7 @@ configure<ApplicationExtension> {
includeInBundle = false
}
testInstrumentationRunner = "com.geeksville.mesh.TestRunner"
testInstrumentationRunner = "org.meshtastic.app.TestRunner"
}
// Configure existing product flavors (defined by convention plugin)
@@ -210,7 +210,6 @@ project.afterEvaluate {
}
dependencies {
implementation(projects.core.analytics)
implementation(projects.core.ble)
implementation(projects.core.common)
implementation(projects.core.data)
@@ -251,10 +250,14 @@ dependencies {
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.paging.compose)
implementation(libs.ktor.client.okhttp)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.coil.network.okhttp)
implementation(libs.coil.svg)
implementation(libs.androidx.core.splashscreen)
implementation(libs.kotlinx.serialization.json)
implementation(libs.okhttp3.logging.interceptor)
implementation(libs.org.eclipse.paho.client.mqttv3)
implementation(libs.usb.serial.android)
implementation(libs.androidx.work.runtime.ktx)
@@ -275,6 +278,16 @@ dependencies {
googleImplementation(libs.location.services)
googleImplementation(libs.play.services.maps)
googleImplementation(libs.dd.sdk.android.okhttp)
googleImplementation(libs.dd.sdk.android.compose)
googleImplementation(libs.dd.sdk.android.logs)
googleImplementation(libs.dd.sdk.android.rum)
googleImplementation(libs.dd.sdk.android.timber)
googleImplementation(libs.dd.sdk.android.trace)
googleImplementation(libs.dd.sdk.android.trace.otel)
googleImplementation(platform(libs.firebase.bom))
googleImplementation(libs.firebase.analytics)
googleImplementation(libs.firebase.crashlytics)
fdroidImplementation(libs.osmdroid.android)
fdroidImplementation(libs.osmdroid.geopackage) { exclude(group = "com.j256.ormlite") }

View File

@@ -2,91 +2,26 @@
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>CommentSpacing:Coroutines.kt$/// Wrap launch with an exception handler, FIXME, move into a utility lib</ID>
<ID>CyclomaticComplexMethod:BleError.kt$BleError.Companion$fun from(exception: Throwable): BleError</ID>
<ID>CyclomaticComplexMethod:MeshMessageProcessor.kt$MeshMessageProcessor$private fun processReceivedMeshPacket(packet: MeshPacket, myNodeNum: Int?)</ID>
<ID>CyclomaticComplexMethod:SettingsNavigation.kt$@Suppress("LongMethod") fun NavGraphBuilder.settingsGraph(navController: NavHostController)</ID>
<ID>EmptyClassBlock:DebugLogFile.kt$BinaryLogFile${ }</ID>
<ID>EmptyFunctionBlock:NopInterface.kt$NopInterface${ }</ID>
<ID>EmptyFunctionBlock:TrustAllX509TrustManager.kt$TrustAllX509TrustManager${}</ID>
<ID>FinalNewline:Coroutines.kt$com.geeksville.mesh.concurrent.Coroutines.kt</ID>
<ID>FinalNewline:DateUtils.kt$com.geeksville.mesh.android.DateUtils.kt</ID>
<ID>FinalNewline:DebugLogFile.kt$com.geeksville.mesh.android.DebugLogFile.kt</ID>
<ID>FinalNewline:InterfaceId.kt$com.geeksville.mesh.repository.radio.InterfaceId.kt</ID>
<ID>FinalNewline:InterfaceSpec.kt$com.geeksville.mesh.repository.radio.InterfaceSpec.kt</ID>
<ID>FinalNewline:MockInterfaceFactory.kt$com.geeksville.mesh.repository.radio.MockInterfaceFactory.kt</ID>
<ID>FinalNewline:NopInterface.kt$com.geeksville.mesh.repository.radio.NopInterface.kt</ID>
<ID>FinalNewline:NopInterfaceFactory.kt$com.geeksville.mesh.repository.radio.NopInterfaceFactory.kt</ID>
<ID>FinalNewline:ProbeTableProvider.kt$com.geeksville.mesh.repository.usb.ProbeTableProvider.kt</ID>
<ID>FinalNewline:SerialConnection.kt$com.geeksville.mesh.repository.usb.SerialConnection.kt</ID>
<ID>FinalNewline:SerialConnectionListener.kt$com.geeksville.mesh.repository.usb.SerialConnectionListener.kt</ID>
<ID>FinalNewline:SerialInterfaceFactory.kt$com.geeksville.mesh.repository.radio.SerialInterfaceFactory.kt</ID>
<ID>FinalNewline:TCPInterfaceFactory.kt$com.geeksville.mesh.repository.radio.TCPInterfaceFactory.kt</ID>
<ID>FinalNewline:UsbRepositoryModule.kt$com.geeksville.mesh.repository.usb.UsbRepositoryModule.kt</ID>
<ID>LongMethod:TCPInterface.kt$TCPInterface$private suspend fun startConnect()</ID>
<ID>MagicNumber:Contacts.kt$7</ID>
<ID>MagicNumber:Contacts.kt$8</ID>
<ID>MagicNumber:MQTTRepository.kt$MQTTRepository$512</ID>
<ID>MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$21972</ID>
<ID>MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$32809</ID>
<ID>MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$6790</ID>
<ID>MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$9114</ID>
<ID>MagicNumber:SerialConnectionImpl.kt$SerialConnectionImpl$115200</ID>
<ID>MagicNumber:SerialConnectionImpl.kt$SerialConnectionImpl$200</ID>
<ID>MagicNumber:ServiceClient.kt$ServiceClient$500</ID>
<ID>MagicNumber:StreamInterface.kt$StreamInterface$0xff</ID>
<ID>MagicNumber:StreamInterface.kt$StreamInterface$3</ID>
<ID>MagicNumber:StreamInterface.kt$StreamInterface$4</ID>
<ID>MagicNumber:StreamInterface.kt$StreamInterface$8</ID>
<ID>MagicNumber:TCPInterface.kt$TCPInterface$1000</ID>
<ID>MagicNumber:UIState.kt$4</ID>
<ID>MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found fromNum: ${fromNumCharacteristic?.uuid}, ${fromNumCharacteristic?.instanceId}"</ID>
<ID>MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found fromRadio: ${fromRadioCharacteristic?.uuid}, ${fromRadioCharacteristic?.instanceId}"</ID>
<ID>MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found logRadio: ${logRadioCharacteristic?.uuid}, ${logRadioCharacteristic?.instanceId}"</ID>
<ID>MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found toRadio: ${toRadioCharacteristic?.uuid}, ${toRadioCharacteristic?.instanceId}"</ID>
<ID>NewLineAtEndOfFile:Coroutines.kt$com.geeksville.mesh.concurrent.Coroutines.kt</ID>
<ID>NewLineAtEndOfFile:DateUtils.kt$com.geeksville.mesh.android.DateUtils.kt</ID>
<ID>NewLineAtEndOfFile:DebugLogFile.kt$com.geeksville.mesh.android.DebugLogFile.kt</ID>
<ID>NewLineAtEndOfFile:InterfaceId.kt$com.geeksville.mesh.repository.radio.InterfaceId.kt</ID>
<ID>NewLineAtEndOfFile:InterfaceSpec.kt$com.geeksville.mesh.repository.radio.InterfaceSpec.kt</ID>
<ID>NewLineAtEndOfFile:MockInterfaceFactory.kt$com.geeksville.mesh.repository.radio.MockInterfaceFactory.kt</ID>
<ID>NewLineAtEndOfFile:NopInterface.kt$com.geeksville.mesh.repository.radio.NopInterface.kt</ID>
<ID>NewLineAtEndOfFile:NopInterfaceFactory.kt$com.geeksville.mesh.repository.radio.NopInterfaceFactory.kt</ID>
<ID>NewLineAtEndOfFile:ProbeTableProvider.kt$com.geeksville.mesh.repository.usb.ProbeTableProvider.kt</ID>
<ID>NewLineAtEndOfFile:SerialConnection.kt$com.geeksville.mesh.repository.usb.SerialConnection.kt</ID>
<ID>NewLineAtEndOfFile:SerialConnectionListener.kt$com.geeksville.mesh.repository.usb.SerialConnectionListener.kt</ID>
<ID>NewLineAtEndOfFile:SerialInterfaceFactory.kt$com.geeksville.mesh.repository.radio.SerialInterfaceFactory.kt</ID>
<ID>NewLineAtEndOfFile:TCPInterfaceFactory.kt$com.geeksville.mesh.repository.radio.TCPInterfaceFactory.kt</ID>
<ID>NewLineAtEndOfFile:UsbRepositoryModule.kt$com.geeksville.mesh.repository.usb.UsbRepositoryModule.kt</ID>
<ID>NoBlankLineBeforeRbrace:DebugLogFile.kt$BinaryLogFile$ </ID>
<ID>NoBlankLineBeforeRbrace:NopInterface.kt$NopInterface$ </ID>
<ID>NoConsecutiveBlankLines:DebugLogFile.kt$ </ID>
<ID>NoEmptyClassBody:DebugLogFile.kt$BinaryLogFile${ }</ID>
<ID>NoSemicolons:DateUtils.kt$DateUtils$;</ID>
<ID>OptionalAbstractKeyword:SyncContinuation.kt$Continuation$abstract</ID>
<ID>RethrowCaughtException:SyncContinuation.kt$Continuation$throw ex</ID>
<ID>ReturnCount:MeshDataHandler.kt$MeshDataHandler$@Suppress("LongMethod") private fun handleStoreForwardPlusPlus(packet: MeshPacket)</ID>
<ID>ReturnCount:MeshDataHandler.kt$MeshDataHandler$private fun shouldBatteryNotificationShow(fromNum: Int, t: Telemetry, myNodeNum: Int): Boolean</ID>
<ID>SwallowedException:Exceptions.kt$ex: Throwable</ID>
<ID>MaxLineLength:DataSourceModule.kt$DataSourceModule$fun</ID>
<ID>ParameterListWrapping:DataSourceModule.kt$DataSourceModule$(impl: BootloaderOtaQuirksJsonDataSourceImpl)</ID>
<ID>SwallowedException:NsdManager.kt$ex: IllegalArgumentException</ID>
<ID>SwallowedException:ServiceClient.kt$ServiceClient$ex: IllegalArgumentException</ID>
<ID>SwallowedException:TCPInterface.kt$TCPInterface$ex: SocketTimeoutException</ID>
<ID>TooGenericExceptionCaught:Exceptions.kt$ex: Throwable</ID>
<ID>TooGenericExceptionCaught:MQTTRepository.kt$MQTTRepository$ex: Exception</ID>
<ID>TooGenericExceptionCaught:MeshDataHandler.kt$MeshDataHandler$e: Exception</ID>
<ID>TooGenericExceptionCaught:MeshService.kt$MeshService$ex: Exception</ID>
<ID>TooGenericExceptionCaught:NordicBleInterface.kt$NordicBleInterface$e: Exception</ID>
<ID>TooGenericExceptionCaught:NordicBleInterface.kt$NordicBleInterface$t: Throwable</ID>
<ID>TooGenericExceptionCaught:RadioInterfaceService.kt$RadioInterfaceService$t: Throwable</ID>
<ID>TooGenericExceptionCaught:SyncContinuation.kt$Continuation$ex: Throwable</ID>
<ID>TooGenericExceptionCaught:TCPInterface.kt$TCPInterface$ex: Throwable</ID>
<ID>TooGenericExceptionThrown:ServiceClient.kt$ServiceClient$throw Exception("Haven't called connect")</ID>
<ID>TooGenericExceptionThrown:ServiceClient.kt$ServiceClient$throw Exception("Service not bound")</ID>
<ID>TooGenericExceptionThrown:SyncContinuation.kt$SyncContinuation$throw Exception("SyncContinuation timeout")</ID>
<ID>TooGenericExceptionThrown:SyncContinuation.kt$SyncContinuation$throw Exception("This shouldn't happen")</ID>
<ID>TooManyFunctions:NordicBleInterface.kt$NordicBleInterface : IRadioInterface</ID>
<ID>TooManyFunctions:RadioInterfaceService.kt$RadioInterfaceService</ID>
<ID>TooManyFunctions:UIState.kt$UIViewModel : ViewModel</ID>
<ID>UtilityClassWithPublicConstructor:NetworkRepositoryModule.kt$NetworkRepositoryModule</ID>
</CurrentIssues>
</SmellBaseline>

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh
package org.meshtastic.app
import android.app.Application
import android.content.Context
@@ -24,7 +23,6 @@ import dagger.hilt.android.testing.HiltTestApplication
@Suppress("unused")
class TestRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
return super.newApplication(cl, HiltTestApplication::class.java.name, context)
}
override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application =
super.newApplication(cl, HiltTestApplication::class.java.name, context)
}

View File

@@ -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 com.geeksville.mesh.filter
package org.meshtastic.app.filter
import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.hilt.android.testing.HiltAndroidRule
@@ -48,6 +48,8 @@ class MessageFilterIntegrationTest {
fun filterPrefsIntegration() = runTest {
filterPrefs.setFilterEnabled(true)
filterPrefs.setFilterWords(setOf("test", "spam"))
// Wait briefly for DataStore to process the writes and flows to emit
kotlinx.coroutines.delay(100)
filterService.rebuildPatterns()
assertTrue(filterService.shouldFilter("this is a test message"))

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,20 +14,18 @@
* 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.analytics
package org.meshtastic.core.analytics.platform
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import co.touchlab.kermit.Logger
import co.touchlab.kermit.Severity
import org.meshtastic.core.analytics.BuildConfig
import org.meshtastic.core.analytics.DataPair
import org.meshtastic.app.BuildConfig
import org.meshtastic.core.repository.DataPair
import org.meshtastic.core.repository.PlatformAnalytics
import javax.inject.Inject
/**
* F-Droid specific implementation of [org.meshtastic.analytics.platform.PlatformAnalytics]. This provides no-op
* implementations for analytics and other platform services.
* F-Droid specific implementation of [PlatformAnalytics]. This provides no-op implementations for analytics and other
* platform services.
*/
class FdroidPlatformAnalytics @Inject constructor() : PlatformAnalytics {
init {
@@ -36,7 +34,7 @@ class FdroidPlatformAnalytics @Inject constructor() : PlatformAnalytics {
// release builds rely on system logging only.
if (BuildConfig.DEBUG) {
Logger.setMinSeverity(Severity.Debug)
Logger.i { "F-Droid platform no-op analytics initialized (Debug mode }." }
Logger.i { "F-Droid platform no-op analytics initialized (Debug mode)." }
} else {
Logger.setMinSeverity(Severity.Info)
Logger.i { "F-Droid platform no-op analytics initialized." }
@@ -48,16 +46,6 @@ class FdroidPlatformAnalytics @Inject constructor() : PlatformAnalytics {
Logger.d { "Set device attributes called: firmwareVersion=$firmwareVersion, deviceHardware=$model" }
}
@Composable
override fun AddNavigationTrackingEffect(navController: NavHostController) {
// No-op for F-Droid, but we can log navigation if needed for debugging
if (BuildConfig.DEBUG) {
navController.addOnDestinationChangedListener { _, destination, _ ->
Logger.d { "Navigation changed to: ${destination.route}" }
}
}
}
override val isPlatformServicesAvailable: Boolean
get() = false

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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.core.network.di
package org.meshtastic.app.di
import dagger.Module
import dagger.Provides
@@ -23,9 +22,9 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.meshtastic.core.common.BuildConfigProvider
import org.meshtastic.core.model.NetworkDeviceHardware
import org.meshtastic.core.model.NetworkFirmwareReleases
import org.meshtastic.core.network.BuildConfig
import org.meshtastic.core.network.service.ApiService
import javax.inject.Singleton
@@ -35,11 +34,11 @@ class FDroidNetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder()
fun provideOkHttpClient(buildConfigProvider: BuildConfigProvider): OkHttpClient = OkHttpClient.Builder()
.addInterceptor(
interceptor =
HttpLoggingInterceptor().apply {
if (BuildConfig.DEBUG) {
if (buildConfigProvider.isDebug) {
setLevel(HttpLoggingInterceptor.Level.BODY)
}
},

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,15 +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.core.analytics.di
package org.meshtastic.app.di
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.meshtastic.core.analytics.platform.FdroidPlatformAnalytics
import org.meshtastic.core.analytics.platform.PlatformAnalytics
import org.meshtastic.app.analytics.FdroidPlatformAnalytics
import org.meshtastic.core.repository.PlatformAnalytics
import javax.inject.Singleton
/** Hilt module to provide the [FdroidPlatformAnalytics] for the fdroid flavor. */

View File

@@ -14,22 +14,18 @@
* 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.analytics.platform
package org.meshtastic.app.analytics
import android.app.Application
import android.content.Context
import android.os.Bundle
import android.provider.Settings
import androidx.compose.runtime.Composable
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavHostController
import co.touchlab.kermit.LogWriter
import co.touchlab.kermit.Severity
import com.datadog.android.Datadog
import com.datadog.android.DatadogSite
import com.datadog.android.compose.ExperimentalTrackingApi
import com.datadog.android.compose.NavigationViewTrackingEffect
import com.datadog.android.core.configuration.Configuration
import com.datadog.android.log.Logger
import com.datadog.android.log.Logs
@@ -38,7 +34,6 @@ import com.datadog.android.privacy.TrackingConsent
import com.datadog.android.rum.GlobalRumMonitor
import com.datadog.android.rum.Rum
import com.datadog.android.rum.RumConfiguration
import com.datadog.android.rum.tracking.AcceptAllNavDestinations
import com.datadog.android.trace.Trace
import com.datadog.android.trace.TraceConfiguration
import com.datadog.android.trace.opentelemetry.DatadogOpenTelemetry
@@ -56,9 +51,10 @@ import io.opentelemetry.api.GlobalOpenTelemetry
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.meshtastic.core.analytics.BuildConfig
import org.meshtastic.core.analytics.DataPair
import org.meshtastic.app.BuildConfig
import org.meshtastic.core.repository.AnalyticsPrefs
import org.meshtastic.core.repository.DataPair
import org.meshtastic.core.repository.PlatformAnalytics
import javax.inject.Inject
import co.touchlab.kermit.Logger as KermitLogger
@@ -174,8 +170,6 @@ constructor(
Trace.enable(traceConfig)
GlobalOpenTelemetry.set(DatadogOpenTelemetry(serviceName = SERVICE_NAME))
// Session Replay disabled to reduce PII collection
}
private fun initCrashlytics(application: Application) {
@@ -243,18 +237,6 @@ constructor(
GlobalRumMonitor.get().addAttribute("device_hardware", model)
}
@OptIn(ExperimentalTrackingApi::class)
@Composable
override fun AddNavigationTrackingEffect(navController: NavHostController) {
if (Datadog.isInitialized()) {
NavigationViewTrackingEffect(
navController = navController,
trackArguments = true,
destinationPredicate = AcceptAllNavDestinations(),
)
}
}
private val isGooglePlayAvailable: Boolean
get() =
GoogleApiAvailabilityLight.getInstance().isGooglePlayServicesAvailable(context).let {
@@ -308,7 +290,7 @@ constructor(
}
private fun String.extractSemanticVersion(): String {
val regex = "^(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?".toRegex()
val regex = "^(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?$".toRegex()
val matchResult = regex.find(this)
return matchResult?.groupValues?.drop(1)?.filter { it.isNotEmpty() }?.joinToString(".") ?: this
}
@@ -317,16 +299,16 @@ constructor(
if (!isFirebaseInitialized) return
val bundle = Bundle()
properties.forEach {
when (it.value) {
is Double -> bundle.putDouble(it.name, it.value)
is Int ->
bundle.putLong(it.name, it.value.toLong()) // Firebase expects Long for integer values in bundles
is Long -> bundle.putLong(it.name, it.value)
is Float -> bundle.putDouble(it.name, it.value.toDouble())
is String -> bundle.putString(it.name, it.value as String?) // Explicitly handle String
else -> bundle.putString(it.name, it.value.toString()) // Fallback for other types
val value = it.value
when (value) {
is Double -> bundle.putDouble(it.name, value)
is Int -> bundle.putLong(it.name, value.toLong()) // Firebase expects Long for integer values in bundles
is Long -> bundle.putLong(it.name, value)
is Float -> bundle.putDouble(it.name, value.toDouble())
is String -> bundle.putString(it.name, value) // Explicitly handle String
else -> bundle.putString(it.name, value.toString()) // Fallback for other types
}
KermitLogger.withTag(TAG).d { "Analytics: track $event (${it.name} : ${it.value})" }
KermitLogger.withTag(TAG).d { "Analytics: track $event (${it.name} : $value)" }
}
Firebase.analytics.logEvent(event, bundle)
}

View File

@@ -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.core.network.di
package org.meshtastic.app.di
import android.content.Context
import com.datadog.android.okhttp.DatadogEventListener
@@ -28,7 +28,7 @@ import dagger.hilt.components.SingletonComponent
import okhttp3.Cache
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.meshtastic.core.network.BuildConfig
import org.meshtastic.core.common.BuildConfigProvider
import org.meshtastic.core.network.service.ApiService
import org.meshtastic.core.network.service.ApiServiceImpl
import java.io.File
@@ -44,7 +44,10 @@ interface GoogleNetworkModule {
companion object {
@Provides
@Singleton
fun provideOkHttpClient(@ApplicationContext context: Context): OkHttpClient = OkHttpClient.Builder()
fun provideOkHttpClient(
@ApplicationContext context: Context,
buildConfigProvider: BuildConfigProvider,
): OkHttpClient = OkHttpClient.Builder()
.cache(
cache =
Cache(
@@ -55,7 +58,7 @@ interface GoogleNetworkModule {
.addInterceptor(
interceptor =
HttpLoggingInterceptor().apply {
if (BuildConfig.DEBUG) {
if (buildConfigProvider.isDebug) {
setLevel(HttpLoggingInterceptor.Level.BODY)
}
},

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,15 +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.core.analytics.di
package org.meshtastic.app.di
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.meshtastic.core.analytics.platform.GooglePlatformAnalytics
import org.meshtastic.core.analytics.platform.PlatformAnalytics
import org.meshtastic.app.analytics.GooglePlatformAnalytics
import org.meshtastic.core.repository.PlatformAnalytics
import javax.inject.Singleton
/** Hilt module to provide the [GooglePlatformAnalytics] for the google flavor. */

View File

@@ -102,7 +102,7 @@
</queries>
<application
android:name="com.geeksville.mesh.MeshUtilApplication"
android:name="org.meshtastic.app.MeshUtilApplication"
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="false"
@@ -152,7 +152,7 @@
<!-- This is the public API for doing mesh radio operations from android apps -->
<service
android:name="com.geeksville.mesh.service.MeshService"
android:name="org.meshtastic.app.service.MeshService"
android:enabled="true"
android:foregroundServiceType="connectedDevice|location"
android:exported="true" tools:ignore="ExportedActivity">
@@ -171,7 +171,7 @@
</service>
<activity
android:name="com.geeksville.mesh.MainActivity"
android:name="org.meshtastic.app.MainActivity"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize"
android:exported="true">
@@ -228,7 +228,7 @@
android:resource="@xml/device_filter" />
</activity>
<receiver android:name="com.geeksville.mesh.service.BootCompleteReceiver"
<receiver android:name="org.meshtastic.app.service.BootCompleteReceiver"
android:exported="false">
<!-- handle boot events -->
<intent-filter>
@@ -252,12 +252,12 @@
android:path="com.geeksville.mesh" /> -->
</intent-filter>
</receiver>
<receiver android:name="com.geeksville.mesh.service.ReplyReceiver" android:exported="false" />
<receiver android:name="com.geeksville.mesh.service.MarkAsReadReceiver" android:exported="false" />
<receiver android:name="com.geeksville.mesh.service.ReactionReceiver" android:exported="false" />
<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="com.geeksville.mesh.widget.LocalStatsWidgetReceiver"
android:name="org.meshtastic.app.widget.LocalStatsWidgetReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

View File

@@ -14,22 +14,22 @@
* 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 com.geeksville.mesh
package org.meshtastic.app
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import com.geeksville.mesh.repository.radio.AndroidRadioInterfaceService
import com.geeksville.mesh.service.AndroidAppWidgetUpdater
import com.geeksville.mesh.service.AndroidMeshLocationManager
import com.geeksville.mesh.service.AndroidMeshWorkerManager
import com.geeksville.mesh.service.MeshServiceNotificationsImpl
import com.geeksville.mesh.service.ServiceBroadcasts
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.meshtastic.app.repository.radio.AndroidRadioInterfaceService
import org.meshtastic.app.service.AndroidAppWidgetUpdater
import org.meshtastic.app.service.AndroidMeshLocationManager
import org.meshtastic.app.service.AndroidMeshWorkerManager
import org.meshtastic.app.service.MeshServiceNotificationsImpl
import org.meshtastic.app.service.ServiceBroadcasts
import org.meshtastic.core.common.BuildConfigProvider
import org.meshtastic.core.di.ProcessLifecycle
import org.meshtastic.core.repository.MeshServiceNotifications

View File

@@ -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 com.geeksville.mesh
package org.meshtastic.app
import android.app.PendingIntent
import android.app.TaskStackBuilder
@@ -43,12 +43,12 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import co.touchlab.kermit.Logger
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.MainScreen
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import no.nordicsemi.kotlin.ble.core.android.AndroidEnvironment
import no.nordicsemi.kotlin.ble.environment.android.compose.LocalEnvironmentOwner
import org.meshtastic.app.model.UIViewModel
import org.meshtastic.app.ui.MainScreen
import org.meshtastic.core.model.util.dispatchMeshtasticUri
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.resources.Res

View File

@@ -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 com.geeksville.mesh
package org.meshtastic.app
import android.content.Context
import android.content.Context.BIND_ABOVE_CLIENT
@@ -23,11 +23,11 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import co.touchlab.kermit.Logger
import com.geeksville.mesh.service.MeshService
import com.geeksville.mesh.service.startService
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.scopes.ActivityScoped
import kotlinx.coroutines.launch
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

View File

@@ -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 com.geeksville.mesh
package org.meshtastic.app
import android.app.Application
import android.appwidget.AppWidgetProviderInfo
@@ -27,8 +27,6 @@ import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import co.touchlab.kermit.Logger
import com.geeksville.mesh.widget.LocalStatsWidgetReceiver
import com.geeksville.mesh.worker.MeshLogCleanupWorker
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
@@ -42,6 +40,8 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout
import no.nordicsemi.kotlin.ble.core.android.AndroidEnvironment
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.MeshLogPrefs
@@ -96,7 +96,7 @@ open class MeshUtilApplication :
val entryPoint =
EntryPointAccessors.fromApplication(
this@MeshUtilApplication,
com.geeksville.mesh.widget.LocalStatsWidget.LocalStatsWidgetEntryPoint::class.java,
org.meshtastic.app.widget.LocalStatsWidget.LocalStatsWidgetEntryPoint::class.java,
)
try {
// Wait for real data for up to 30 seconds before pushing an updated preview

View File

@@ -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.core.di
package org.meshtastic.app.di
import android.content.Context
import androidx.work.WorkManager
@@ -24,6 +24,7 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.Dispatchers
import org.meshtastic.core.di.CoroutineDispatchers
import javax.inject.Singleton
@Module

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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.core.data.di
package org.meshtastic.app.di
import android.content.Context
import android.location.LocationManager

View File

@@ -0,0 +1,47 @@
/*
* 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.app.di
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.meshtastic.core.data.datasource.BootloaderOtaQuirksJsonDataSource
import org.meshtastic.core.data.datasource.BootloaderOtaQuirksJsonDataSourceImpl
import org.meshtastic.core.data.datasource.DeviceHardwareJsonDataSource
import org.meshtastic.core.data.datasource.DeviceHardwareJsonDataSourceImpl
import org.meshtastic.core.data.datasource.FirmwareReleaseJsonDataSource
import org.meshtastic.core.data.datasource.FirmwareReleaseJsonDataSourceImpl
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
interface DataSourceModule {
@Binds
@Singleton
fun bindDeviceHardwareJsonDataSource(impl: DeviceHardwareJsonDataSourceImpl): DeviceHardwareJsonDataSource
@Binds
@Singleton
fun bindFirmwareReleaseJsonDataSource(impl: FirmwareReleaseJsonDataSourceImpl): FirmwareReleaseJsonDataSource
@Binds
@Singleton
fun bindBootloaderOtaQuirksJsonDataSource(
impl: BootloaderOtaQuirksJsonDataSourceImpl,
): BootloaderOtaQuirksJsonDataSource
}

View File

@@ -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.core.data.di
package org.meshtastic.app.di
import android.content.Context
import androidx.datastore.core.DataStore

View File

@@ -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.core.data.di
package org.meshtastic.app.di
import dagger.Binds
import dagger.Module

View File

@@ -0,0 +1,96 @@
/*
* 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.app.di
import android.content.Context
import coil3.ImageLoader
import coil3.disk.DiskCache
import coil3.memory.MemoryCache
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
import coil3.request.crossfade
import coil3.svg.SvgDecoder
import coil3.util.DebugLogger
import coil3.util.Logger
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import io.ktor.client.HttpClient
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import org.meshtastic.core.common.BuildConfigProvider
import javax.inject.Singleton
private const val DISK_CACHE_PERCENT = 0.02
private const val MEMORY_CACHE_PERCENT = 0.25
@InstallIn(SingletonComponent::class)
@Module
interface NetworkModule {
@Binds
@Singleton
fun bindMqttRepository(
impl: org.meshtastic.core.network.repository.MQTTRepositoryImpl,
): org.meshtastic.core.network.repository.MQTTRepository
companion object {
@Provides
@Singleton
fun provideImageLoader(
okHttpClient: OkHttpClient,
@ApplicationContext application: Context,
buildConfigProvider: BuildConfigProvider,
): ImageLoader {
val sharedOkHttp = okHttpClient.newBuilder().build()
return ImageLoader.Builder(context = application)
.components {
add(OkHttpNetworkFetcherFactory(callFactory = { sharedOkHttp }))
add(SvgDecoder.Factory(scaleToDensity = true))
}
.memoryCache {
MemoryCache.Builder().maxSizePercent(context = application, percent = MEMORY_CACHE_PERCENT).build()
}
.diskCache { DiskCache.Builder().maxSizePercent(percent = DISK_CACHE_PERCENT).build() }
.logger(
logger = if (buildConfigProvider.isDebug) DebugLogger(minLevel = Logger.Level.Verbose) else null,
)
.crossfade(enable = true)
.build()
}
@Provides
@Singleton
fun provideJson(): Json = Json {
isLenient = true
ignoreUnknownKeys = true
}
@Provides
@Singleton
fun provideHttpClient(okHttpClient: OkHttpClient, json: Json): HttpClient = HttpClient(engineFactory = OkHttp) {
engine { preconfigured = okHttpClient }
install(plugin = ContentNegotiation) { json(json) }
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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.core.data.di
package org.meshtastic.app.di
import dagger.Binds
import dagger.Module

View File

@@ -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.core.prefs.di
package org.meshtastic.app.di
import android.content.Context
import androidx.datastore.core.DataStore
@@ -32,6 +32,18 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import org.meshtastic.core.prefs.analytics.AnalyticsPrefsImpl
import org.meshtastic.core.prefs.di.AnalyticsDataStore
import org.meshtastic.core.prefs.di.AppDataStore
import org.meshtastic.core.prefs.di.CustomEmojiDataStore
import org.meshtastic.core.prefs.di.FilterDataStore
import org.meshtastic.core.prefs.di.HomoglyphEncodingDataStore
import org.meshtastic.core.prefs.di.MapConsentDataStore
import org.meshtastic.core.prefs.di.MapDataStore
import org.meshtastic.core.prefs.di.MapTileProviderDataStore
import org.meshtastic.core.prefs.di.MeshDataStore
import org.meshtastic.core.prefs.di.MeshLogDataStore
import org.meshtastic.core.prefs.di.RadioDataStore
import org.meshtastic.core.prefs.di.UiDataStore
import org.meshtastic.core.prefs.emoji.CustomEmojiPrefsImpl
import org.meshtastic.core.prefs.filter.FilterPrefsImpl
import org.meshtastic.core.prefs.homoglyph.HomoglyphPrefsImpl

View File

@@ -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.core.data.di
package org.meshtastic.app.di
import dagger.Binds
import dagger.Module
@@ -38,6 +38,7 @@ import org.meshtastic.core.data.manager.NodeManagerImpl
import org.meshtastic.core.data.manager.PacketHandlerImpl
import org.meshtastic.core.data.manager.TracerouteHandlerImpl
import org.meshtastic.core.data.repository.DeviceHardwareRepositoryImpl
import org.meshtastic.core.data.repository.LocationRepositoryImpl
import org.meshtastic.core.data.repository.MeshLogRepositoryImpl
import org.meshtastic.core.data.repository.NodeRepositoryImpl
import org.meshtastic.core.data.repository.PacketRepositoryImpl
@@ -47,6 +48,7 @@ import org.meshtastic.core.repository.CommandSender
import org.meshtastic.core.repository.DeviceHardwareRepository
import org.meshtastic.core.repository.FromRadioPacketHandler
import org.meshtastic.core.repository.HistoryManager
import org.meshtastic.core.repository.LocationRepository
import org.meshtastic.core.repository.MeshActionHandler
import org.meshtastic.core.repository.MeshConfigFlowManager
import org.meshtastic.core.repository.MeshConfigHandler
@@ -78,6 +80,10 @@ abstract class RepositoryModule {
@Singleton
abstract fun bindRadioConfigRepository(radioConfigRepositoryImpl: RadioConfigRepositoryImpl): RadioConfigRepository
@Binds
@Singleton
abstract fun bindLocationRepository(locationRepositoryImpl: LocationRepositoryImpl): LocationRepository
@Binds
@Singleton
abstract fun bindDeviceHardwareRepository(

View File

@@ -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.core.data.di
package org.meshtastic.app.di
import dagger.Module
import dagger.Provides

View File

@@ -14,19 +14,19 @@
* 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 com.geeksville.mesh.domain.usecase
package org.meshtastic.app.domain.usecase
import android.hardware.usb.UsbManager
import android.net.nsd.NsdServiceInfo
import com.geeksville.mesh.model.DeviceListEntry
import com.geeksville.mesh.model.getMeshtasticShortName
import com.geeksville.mesh.repository.network.NetworkRepository
import com.geeksville.mesh.repository.network.NetworkRepository.Companion.toAddressString
import com.geeksville.mesh.repository.usb.UsbRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import org.jetbrains.compose.resources.getString
import org.meshtastic.app.model.DeviceListEntry
import org.meshtastic.app.model.getMeshtasticShortName
import org.meshtastic.app.repository.network.NetworkRepository
import org.meshtastic.app.repository.network.NetworkRepository.Companion.toAddressString
import org.meshtastic.app.repository.usb.UsbRepository
import org.meshtastic.core.ble.BluetoothRepository
import org.meshtastic.core.database.DatabaseManager
import org.meshtastic.core.datastore.RecentAddressesDataSource

View File

@@ -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 com.geeksville.mesh.model
package org.meshtastic.app.model
import android.hardware.usb.UsbManager
import com.hoho.android.usbserial.driver.UsbSerialDriver

View File

@@ -14,21 +14,17 @@
* 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 com.geeksville.mesh.model
package org.meshtastic.app.model
import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import co.touchlab.kermit.Logger
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -37,10 +33,8 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.getString
import org.meshtastic.core.analytics.platform.PlatformAnalytics
import org.meshtastic.core.data.repository.FirmwareReleaseRepository
import org.meshtastic.core.database.entity.asDeviceVersion
import org.meshtastic.core.datastore.UiPreferencesDataSource
@@ -83,7 +77,6 @@ constructor(
firmwareReleaseRepository: FirmwareReleaseRepository,
private val uiPreferencesDataSource: UiPreferencesDataSource,
private val meshServiceNotifications: MeshServiceNotifications,
private val analytics: PlatformAnalytics,
packetRepository: PacketRepository,
private val alertManager: AlertManager,
) : ViewModel() {
@@ -99,12 +92,8 @@ constructor(
meshServiceNotifications.clearClientNotification(notification)
}
/**
* Emits events for mesh network send/receive activity. This is a SharedFlow to ensure all events are delivered,
* even if they are the same.
*/
val meshActivity: SharedFlow<MeshActivity> =
radioInterfaceService.meshActivity.shareIn(viewModelScope, SharingStarted.Eagerly, 0)
/** Emits events for mesh network send/receive activity. */
val meshActivity: Flow<MeshActivity> = radioInterfaceService.meshActivity
private val _scrollToTopEventFlow =
MutableSharedFlow<ScrollToTopEvent>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
@@ -276,9 +265,4 @@ constructor(
fun onAppIntroCompleted() {
uiPreferencesDataSource.setAppIntroCompleted(true)
}
@Composable
fun AddNavigationTrackingEffect(navController: NavHostController) {
analytics.AddNavigationTrackingEffect(navController)
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh.navigation
package org.meshtastic.app.navigation
import androidx.compose.runtime.remember
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
@@ -24,7 +23,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import androidx.navigation.navigation
import com.geeksville.mesh.ui.sharing.ChannelScreen
import org.meshtastic.app.ui.sharing.ChannelScreen
import org.meshtastic.core.navigation.ChannelsRoutes
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.navigation.SettingsRoutes

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh.navigation
package org.meshtastic.app.navigation
import androidx.compose.runtime.remember
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
@@ -24,7 +23,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import androidx.navigation.navigation
import com.geeksville.mesh.ui.connections.ConnectionsScreen
import org.meshtastic.app.ui.connections.ConnectionsScreen
import org.meshtastic.core.navigation.ConnectionsRoutes
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.navigation.NodesRoutes

View File

@@ -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 com.geeksville.mesh.navigation
package org.meshtastic.app.navigation
import androidx.compose.runtime.getValue
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
@@ -25,8 +25,8 @@ import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import androidx.navigation.navigation
import androidx.navigation.toRoute
import com.geeksville.mesh.model.UIViewModel
import kotlinx.coroutines.flow.Flow
import org.meshtastic.app.model.UIViewModel
import org.meshtastic.core.navigation.ContactsRoutes
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.ui.component.ScrollToTopEvent

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh.navigation
package org.meshtastic.app.navigation
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh.navigation
package org.meshtastic.app.navigation
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController

View File

@@ -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 com.geeksville.mesh.navigation
package org.meshtastic.app.navigation
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.CellTower
@@ -38,9 +38,9 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.navigation
import androidx.navigation.navDeepLink
import androidx.navigation.toRoute
import com.geeksville.mesh.ui.node.AdaptiveNodeListScreen
import kotlinx.coroutines.flow.Flow
import org.jetbrains.compose.resources.StringResource
import org.meshtastic.app.ui.node.AdaptiveNodeListScreen
import org.meshtastic.core.navigation.ContactsRoutes
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.navigation.NodeDetailRoutes

View File

@@ -16,7 +16,7 @@
*/
@file:Suppress("Wrapping", "SpacingAroundColon")
package com.geeksville.mesh.navigation
package org.meshtastic.app.navigation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh.repository.network
package org.meshtastic.app.repository.network
import android.net.ConnectivityManager
import android.net.Network
@@ -27,9 +26,8 @@ import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
internal fun ConnectivityManager.networkAvailable(): Flow<Boolean> = observeNetworks()
.map { activeNetworksList -> activeNetworksList.isNotEmpty() }
.distinctUntilChanged()
internal fun ConnectivityManager.networkAvailable(): Flow<Boolean> =
observeNetworks().map { activeNetworksList -> activeNetworksList.isNotEmpty() }.distinctUntilChanged()
internal fun ConnectivityManager.observeNetworks(
networkRequest: NetworkRequest = NetworkRequest.Builder().build(),
@@ -37,30 +35,26 @@ internal fun ConnectivityManager.observeNetworks(
// Keep track of the current active networks
val activeNetworks = mutableSetOf<Network>()
val callback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
activeNetworks.add(network)
trySend(activeNetworks.toList())
}
override fun onLost(network: Network) {
activeNetworks.remove(network)
trySend(activeNetworks.toList())
}
override fun onCapabilitiesChanged(
network: Network,
networkCapabilities: NetworkCapabilities
) {
if (activeNetworks.contains(network)) {
val callback =
object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
activeNetworks.add(network)
trySend(activeNetworks.toList())
}
override fun onLost(network: Network) {
activeNetworks.remove(network)
trySend(activeNetworks.toList())
}
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
if (activeNetworks.contains(network)) {
trySend(activeNetworks.toList())
}
}
}
}
registerNetworkCallback(networkRequest, callback)
awaitClose {
unregisterNetworkCallback(callback)
}
awaitClose { unregisterNetworkCallback(callback) }
}

View File

@@ -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 com.geeksville.mesh.repository.network
package org.meshtastic.app.repository.network
import android.net.ConnectivityManager
import android.net.nsd.NsdManager

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh.repository.network
package org.meshtastic.app.repository.network
import android.app.Application
import android.content.Context
@@ -31,13 +30,11 @@ import dagger.hilt.components.SingletonComponent
class NetworkRepositoryModule {
companion object {
@Provides
fun provideConnectivityManager(application: Application): ConnectivityManager {
return application.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
}
fun provideConnectivityManager(application: Application): ConnectivityManager =
application.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
@Provides
fun provideNsdManager(application: Application): NsdManager {
return application.getSystemService(Context.NSD_SERVICE) as NsdManager
}
fun provideNsdManager(application: Application): NsdManager =
application.getSystemService(Context.NSD_SERVICE) as NsdManager
}
}

View File

@@ -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 com.geeksville.mesh.repository.network
package org.meshtastic.app.repository.network
import android.annotation.SuppressLint
import android.net.nsd.NsdManager

View File

@@ -14,15 +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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import android.app.Application
import android.provider.Settings
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import co.touchlab.kermit.Logger
import com.geeksville.mesh.BuildConfig
import com.geeksville.mesh.repository.network.NetworkRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
@@ -37,10 +35,10 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.meshtastic.core.analytics.platform.PlatformAnalytics
import org.meshtastic.app.BuildConfig
import org.meshtastic.app.repository.network.NetworkRepository
import org.meshtastic.core.ble.BluetoothRepository
import org.meshtastic.core.common.util.BinaryLogFile
import org.meshtastic.core.common.util.BuildUtils
import org.meshtastic.core.common.util.handledLaunch
import org.meshtastic.core.common.util.ignoreException
import org.meshtastic.core.common.util.nowMillis
@@ -51,6 +49,7 @@ 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.repository.PlatformAnalytics
import org.meshtastic.core.repository.RadioInterfaceService
import org.meshtastic.core.repository.RadioPrefs
import org.meshtastic.proto.Heartbeat
@@ -125,6 +124,15 @@ constructor(
if (listenersInitialized) return
listenersInitialized = true
radioPrefs.devAddr
.onEach { addr ->
if (_currentDeviceAddressFlow.value != addr) {
_currentDeviceAddressFlow.value = addr
startInterface()
}
}
.launchIn(processLifecycle.coroutineScope)
bluetoothRepository.state
.onEach { state ->
if (state.enabled) {
@@ -176,31 +184,9 @@ constructor(
override fun isMockInterface(): Boolean =
BuildConfig.DEBUG || Settings.System.getString(context.contentResolver, "firebase.test.lab") == "true"
/**
* Determines whether to default to mock interface for device address. This keeps the decision logic separate and
* easy to extend.
*/
private fun shouldDefaultToMockInterface(): Boolean = BuildUtils.isEmulator
/**
* Return the device we are configured to use, or null for none device address strings are of the form:
*
* at
*
* where a is either x for bluetooth or s for serial and t is an interface specific address (macaddr or a device
* path)
*/
override fun getDeviceAddress(): String? {
// If the user has unpaired our device, treat things as if we don't have one
var address = radioPrefs.devAddr.value
// If we are running on the emulator we default to the mock interface, so we can have some data to show to the
// user
if (address == null && shouldDefaultToMockInterface()) {
address = mockInterfaceAddress
}
return address
return _currentDeviceAddressFlow.value
}
/**
@@ -380,21 +366,18 @@ constructor(
_serviceScope.handledLaunch { handleSendToRadio(bytes) }
}
private val _meshActivity = MutableSharedFlow<MeshActivity>(extraBufferCapacity = 64)
private val _meshActivity =
MutableSharedFlow<MeshActivity>(
extraBufferCapacity = 64,
onBufferOverflow = kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST,
)
override val meshActivity: SharedFlow<MeshActivity> = _meshActivity.asSharedFlow()
private fun emitSendActivity() {
// Use tryEmit for SharedFlow as it's non-blocking
val emitted = _meshActivity.tryEmit(MeshActivity.Send)
if (!emitted) {
Logger.d { "MeshActivity.Send event was not emitted due to buffer overflow or no collectors" }
}
_meshActivity.tryEmit(MeshActivity.Send)
}
private fun emitReceiveActivity() {
val emitted = _meshActivity.tryEmit(MeshActivity.Receive)
if (!emitted) {
Logger.d { "MeshActivity.Receive event was not emitted due to buffer overflow or no collectors" }
}
_meshActivity.tryEmit(MeshActivity.Receive)
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import java.io.Closeable

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import org.meshtastic.core.model.InterfaceId
import javax.inject.Inject

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,17 +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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
/**
* Radio interface factory service provider interface. Each radio backend implementation needs
* to have a factory to create new instances. These instances are specific to a particular
* address. This interface defines a common API across all radio interfaces for obtaining
* implementation instances.
* Radio interface factory service provider interface. Each radio backend implementation needs to have a factory to
* create new instances. These instances are specific to a particular address. This interface defines a common API
* across all radio interfaces for obtaining implementation instances.
*
* This is primarily used in conjunction with Dagger assisted injection for each backend
* interface type.
* This is primarily used in conjunction with Dagger assisted injection for each backend interface type.
*/
interface InterfaceFactorySpi<T : IRadioInterface> {
fun create(rest: String): T

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import dagger.MapKey
import org.meshtastic.core.model.InterfaceId

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,15 +14,12 @@
* 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 com.geeksville.mesh.repository.radio
/**
* This interface defines the contract that all radio backend implementations must adhere to.
*/
/** This interface defines the contract that all radio backend implementations must adhere to. */
interface InterfaceSpec<T : IRadioInterface> {
fun createInterface(rest: String): T
/** Return true if this address is still acceptable. For BLE that means, still bonded */
fun addressValid(rest: String): Boolean = true
}
}

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import kotlinx.coroutines.flow.Flow

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import co.touchlab.kermit.Logger
import dagger.assisted.Assisted

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,15 +14,12 @@
* 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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import dagger.assisted.AssistedFactory
/**
* Factory for creating `MockInterface` instances.
*/
/** Factory for creating `MockInterface` instances. */
@AssistedFactory
interface MockInterfaceFactory {
fun create(rest: String): MockInterface
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,20 +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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import javax.inject.Inject
/**
* Mock interface backend implementation.
*/
class MockInterfaceSpec @Inject constructor(
private val factory: MockInterfaceFactory
) : InterfaceSpec<MockInterface> {
override fun createInterface(rest: String): MockInterface {
return factory.create(rest)
}
/** Mock interface backend implementation. */
class MockInterfaceSpec @Inject constructor(private val factory: MockInterfaceFactory) : InterfaceSpec<MockInterface> {
override fun createInterface(rest: String): MockInterface = factory.create(rest)
/** Return true if this address is still acceptable. For BLE that means, still bonded */
override fun addressValid(rest: String): Boolean = true

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,17 +14,17 @@
* 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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
class NopInterface @AssistedInject constructor(@Assisted val address: String) : IRadioInterface {
override fun handleSendToRadio(p: ByteArray) {
// No-op
}
override fun close() {
// No-op
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,15 +14,12 @@
* 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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import dagger.assisted.AssistedFactory
/**
* Factory for creating `NopInterface` instances.
*/
/** Factory for creating `NopInterface` instances. */
@AssistedFactory
interface NopInterfaceFactory {
fun create(rest: String): NopInterface
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,18 +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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import javax.inject.Inject
/**
* No-op interface backend implementation.
*/
class NopInterfaceSpec @Inject constructor(
private val factory: NopInterfaceFactory
) : InterfaceSpec<NopInterface> {
override fun createInterface(rest: String): NopInterface {
return factory.create(rest)
}
/** No-op interface backend implementation. */
class NopInterfaceSpec @Inject constructor(private val factory: NopInterfaceFactory) : InterfaceSpec<NopInterface> {
override fun createInterface(rest: String): NopInterface = factory.create(rest)
}

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import android.annotation.SuppressLint
import co.touchlab.kermit.Logger

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import dagger.assisted.AssistedFactory

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import co.touchlab.kermit.Logger
import org.meshtastic.core.ble.BluetoothRepository

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import dagger.Binds
import dagger.Module

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import co.touchlab.kermit.Logger
import com.geeksville.mesh.repository.usb.SerialConnection
import com.geeksville.mesh.repository.usb.SerialConnectionListener
import com.geeksville.mesh.repository.usb.UsbRepository
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import org.meshtastic.app.repository.usb.SerialConnection
import org.meshtastic.app.repository.usb.SerialConnectionListener
import org.meshtastic.app.repository.usb.UsbRepository
import org.meshtastic.core.common.util.nowMillis
import org.meshtastic.core.repository.RadioInterfaceService
import java.util.concurrent.atomic.AtomicReference

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,15 +14,12 @@
* 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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import dagger.assisted.AssistedFactory
/**
* Factory for creating `SerialInterface` instances.
*/
/** Factory for creating `SerialInterface` instances. */
@AssistedFactory
interface SerialInterfaceFactory {
fun create(rest: String): SerialInterface
}
}

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import android.hardware.usb.UsbManager
import com.geeksville.mesh.repository.usb.UsbRepository
import com.hoho.android.usbserial.driver.UsbSerialDriver
import org.meshtastic.app.repository.usb.UsbRepository
import javax.inject.Inject
/** Serial/USB interface backend implementation. */

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import co.touchlab.kermit.Logger
import kotlinx.coroutines.launch

View File

@@ -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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import co.touchlab.kermit.Logger
import com.geeksville.mesh.repository.network.NetworkRepository
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import org.meshtastic.app.repository.network.NetworkRepository
import org.meshtastic.core.common.util.Exceptions
import org.meshtastic.core.common.util.handledLaunch
import org.meshtastic.core.common.util.nowMillis

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,15 +14,12 @@
* 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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import dagger.assisted.AssistedFactory
/**
* Factory for creating `TCPInterface` instances.
*/
/** Factory for creating `TCPInterface` instances. */
@AssistedFactory
interface TCPInterfaceFactory {
fun create(rest: String): TCPInterface
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,18 +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 com.geeksville.mesh.repository.radio
package org.meshtastic.app.repository.radio
import javax.inject.Inject
/**
* TCP interface backend implementation.
*/
class TCPInterfaceSpec @Inject constructor(
private val factory: TCPInterfaceFactory
) : InterfaceSpec<TCPInterface> {
override fun createInterface(rest: String): TCPInterface {
return factory.create(rest)
}
/** TCP interface backend implementation. */
class TCPInterfaceSpec @Inject constructor(private val factory: TCPInterfaceFactory) : InterfaceSpec<TCPInterface> {
override fun createInterface(rest: String): TCPInterface = factory.create(rest)
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh.repository.usb
package org.meshtastic.app.repository.usb
import com.hoho.android.usbserial.driver.CdcAcmSerialDriver
import com.hoho.android.usbserial.driver.ProbeTable
@@ -25,18 +24,15 @@ import javax.inject.Inject
import javax.inject.Provider
/**
* Creates a probe table for the USB driver. This augments the default device-to-driver
* mappings with additional known working configurations. See this package's README for
* more info.
* Creates a probe table for the USB driver. This augments the default device-to-driver mappings with additional known
* working configurations. See this package's README for more info.
*/
@Reusable
class ProbeTableProvider @Inject constructor() : Provider<ProbeTable> {
override fun get(): ProbeTable {
return UsbSerialProber.getDefaultProbeTable().apply {
// RAK 4631:
addProduct(9114, 32809, CdcAcmSerialDriver::class.java)
// LilyGo TBeam v1.1:
addProduct(6790, 21972, CdcAcmSerialDriver::class.java)
}
override fun get(): ProbeTable = UsbSerialProber.getDefaultProbeTable().apply {
// RAK 4631:
addProduct(9114, 32809, CdcAcmSerialDriver::class.java)
// LilyGo TBeam v1.1:
addProduct(6790, 21972, CdcAcmSerialDriver::class.java)
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,21 +14,16 @@
* 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.usb
package com.geeksville.mesh.repository.usb
/**
* USB serial connection.
*/
/** USB serial connection. */
interface SerialConnection : AutoCloseable {
/**
* Called to initiate the serial connection.
*/
/** Called to initiate the serial connection. */
fun connect()
/**
* Send data (asynchronously) to the serial device. If the connection is not presently
* established then the data provided is ignored / dropped.
* Send data (asynchronously) to the serial device. If the connection is not presently established then the data
* provided is ignored / dropped.
*/
fun sendBytes(bytes: ByteArray)
@@ -40,4 +35,4 @@ interface SerialConnection : AutoCloseable {
fun close(waitForStopped: Boolean)
override fun close()
}
}

View File

@@ -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 com.geeksville.mesh.repository.usb
package org.meshtastic.app.repository.usb
import android.hardware.usb.UsbManager
import co.touchlab.kermit.Logger

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,31 +14,19 @@
* 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.usb
package com.geeksville.mesh.repository.usb
/**
* Callbacks indicating state changes in the USB serial connection.
*/
/** Callbacks indicating state changes in the USB serial connection. */
interface SerialConnectionListener {
/**
* Unable to initiate the connection due to missing permissions. This is a terminal
* state.
*/
/** Unable to initiate the connection due to missing permissions. This is a terminal state. */
fun onMissingPermission() {}
/**
* Called when a connection has been established.
*/
/** Called when a connection has been established. */
fun onConnected() {}
/**
* Called when serial data is received.
*/
/** Called when serial data is received. */
fun onDataReceived(bytes: ByteArray) {}
/**
* Called when the connection has been terminated.
*/
/** Called when the connection has been terminated. */
fun onDisconnected(thrown: Exception?) {}
}
}

View File

@@ -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 com.geeksville.mesh.repository.usb
package org.meshtastic.app.repository.usb
import android.content.BroadcastReceiver
import android.content.Context

View File

@@ -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 com.geeksville.mesh.repository.usb
package org.meshtastic.app.repository.usb
import android.content.BroadcastReceiver
import android.content.Context

View File

@@ -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 com.geeksville.mesh.repository.usb
package org.meshtastic.app.repository.usb
import android.app.Application
import android.hardware.usb.UsbDevice

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh.repository.usb
package org.meshtastic.app.repository.usb
import android.app.Application
import android.content.Context
@@ -35,10 +34,8 @@ interface UsbRepositoryModule {
fun provideUsbManager(application: Application): UsbManager? =
application.getSystemService(Context.USB_SERVICE) as UsbManager?
@Provides
fun provideProbeTable(provider: ProbeTableProvider): ProbeTable = provider.get()
@Provides fun provideProbeTable(provider: ProbeTableProvider): ProbeTable = provider.get()
@Provides
fun provideUsbSerialProber(probeTable: ProbeTable): UsbSerialProber = UsbSerialProber(probeTable)
@Provides fun provideUsbSerialProber(probeTable: ProbeTable): UsbSerialProber = UsbSerialProber(probeTable)
}
}
}

View File

@@ -14,12 +14,12 @@
* 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 com.geeksville.mesh.service
package org.meshtastic.app.service
import android.content.Context
import androidx.glance.appwidget.updateAll
import com.geeksville.mesh.widget.LocalStatsWidget
import dagger.hilt.android.qualifiers.ApplicationContext
import org.meshtastic.app.widget.LocalStatsWidget
import org.meshtastic.core.repository.AppWidgetUpdater
import javax.inject.Inject
import javax.inject.Singleton

View File

@@ -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 com.geeksville.mesh.service
package org.meshtastic.app.service
import android.annotation.SuppressLint
import android.app.Application
@@ -27,8 +27,8 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.meshtastic.core.common.hasLocationPermission
import org.meshtastic.core.data.repository.LocationRepository
import org.meshtastic.core.model.Position
import org.meshtastic.core.repository.LocationRepository
import org.meshtastic.core.repository.MeshLocationManager
import javax.inject.Inject
import javax.inject.Singleton

View File

@@ -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 com.geeksville.mesh.service
package org.meshtastic.app.service
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,8 +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 com.geeksville.mesh.service
package org.meshtastic.app.service
import android.content.BroadcastReceiver
import android.content.Context

View File

@@ -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 com.geeksville.mesh.service
package org.meshtastic.app.service
import org.meshtastic.core.api.MeshtasticIntent

View File

@@ -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 com.geeksville.mesh.service
package org.meshtastic.app.service
import android.content.BroadcastReceiver
import android.content.Context

View File

@@ -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 com.geeksville.mesh.service
package org.meshtastic.app.service
import android.app.Service
import android.content.Context
@@ -24,14 +24,14 @@ import android.os.Build
import android.os.IBinder
import androidx.core.app.ServiceCompat
import co.touchlab.kermit.Logger
import com.geeksville.mesh.BuildConfig
import com.geeksville.mesh.ui.connections.NO_DEVICE_SELECTED
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.meshtastic.app.BuildConfig
import org.meshtastic.app.ui.connections.NO_DEVICE_SELECTED
import org.meshtastic.core.common.hasLocationPermission
import org.meshtastic.core.common.util.handledLaunch
import org.meshtastic.core.common.util.toRemoteExceptions

View File

@@ -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 com.geeksville.mesh.service
package org.meshtastic.app.service
import android.app.Notification
import android.app.NotificationChannel
@@ -36,16 +36,16 @@ import androidx.core.content.getSystemService
import androidx.core.graphics.createBitmap
import androidx.core.graphics.drawable.IconCompat
import androidx.core.net.toUri
import com.geeksville.mesh.MainActivity
import com.geeksville.mesh.R.raw
import com.geeksville.mesh.service.MarkAsReadReceiver.Companion.MARK_AS_READ_ACTION
import com.geeksville.mesh.service.ReactionReceiver.Companion.REACT_ACTION
import com.geeksville.mesh.service.ReplyReceiver.Companion.KEY_TEXT_REPLY
import dagger.Lazy
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.jetbrains.compose.resources.StringResource
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
@@ -459,7 +459,7 @@ constructor(
val summaryNotification =
commonBuilder(NotificationType.DirectMessage)
.setSmallIcon(com.geeksville.mesh.R.drawable.app_icon)
.setSmallIcon(org.meshtastic.app.R.drawable.app_icon)
.setStyle(messagingStyle)
.setGroup(GROUP_KEY_MESSAGES)
.setGroupSummary(true)
@@ -817,7 +817,7 @@ constructor(
type: NotificationType,
contentIntent: PendingIntent? = null,
): NotificationCompat.Builder {
val smallIcon = com.geeksville.mesh.R.drawable.app_icon
val smallIcon = org.meshtastic.app.R.drawable.app_icon
return NotificationCompat.Builder(context, type.channelId)
.setSmallIcon(smallIcon)

View File

@@ -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 com.geeksville.mesh.service
package org.meshtastic.app.service
import android.app.ForegroundServiceStartNotAllowedException
import android.content.Context
@@ -23,8 +23,8 @@ import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import co.touchlab.kermit.Logger
import com.geeksville.mesh.BuildConfig
import com.geeksville.mesh.worker.ServiceKeepAliveWorker
import org.meshtastic.app.BuildConfig
import org.meshtastic.app.worker.ServiceKeepAliveWorker
// / Helper function to start running our service
fun MeshService.Companion.startService(context: Context) {

View File

@@ -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 com.geeksville.mesh.service
package org.meshtastic.app.service
import android.content.BroadcastReceiver
import android.content.Context
@@ -54,7 +54,7 @@ class ReactionReceiver : BroadcastReceiver() {
}
companion object {
const val REACT_ACTION = "com.geeksville.mesh.REACT_ACTION"
const val REACT_ACTION = "org.meshtastic.app.REACT_ACTION"
const val EXTRA_CONTACT_KEY = "extra_contact_key"
const val EXTRA_REACTION = "extra_reaction"
const val EXTRA_REPLY_ID = "extra_reply_id"

View File

@@ -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 com.geeksville.mesh.service
package org.meshtastic.app.service
import android.content.BroadcastReceiver
import android.content.Context
@@ -47,7 +47,7 @@ class ReplyReceiver : BroadcastReceiver() {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
companion object {
const val REPLY_ACTION = "com.geeksville.mesh.REPLY_ACTION"
const val REPLY_ACTION = "org.meshtastic.app.REPLY_ACTION"
const val CONTACT_KEY = "contactKey"
const val KEY_TEXT_REPLY = "key_text_reply"
}

View File

@@ -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 com.geeksville.mesh.service
package org.meshtastic.app.service
import android.content.Context
import android.content.Intent

View File

@@ -16,7 +16,7 @@
*/
@file:Suppress("MatchingDeclarationName")
package com.geeksville.mesh.ui
package org.meshtastic.app.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
@@ -77,25 +77,25 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import co.touchlab.kermit.Logger
import com.geeksville.mesh.BuildConfig
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.navigation.channelsGraph
import com.geeksville.mesh.navigation.connectionsGraph
import com.geeksville.mesh.navigation.contactsGraph
import com.geeksville.mesh.navigation.firmwareGraph
import com.geeksville.mesh.navigation.mapGraph
import com.geeksville.mesh.navigation.nodesGraph
import com.geeksville.mesh.navigation.settingsGraph
import com.geeksville.mesh.service.MeshService
import com.geeksville.mesh.ui.connections.DeviceType
import com.geeksville.mesh.ui.connections.ScannerViewModel
import com.geeksville.mesh.ui.connections.components.ConnectionsNavIcon
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import no.nordicsemi.android.common.permissions.notification.RequestNotificationPermission
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.getString
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.app.BuildConfig
import org.meshtastic.app.model.UIViewModel
import org.meshtastic.app.navigation.channelsGraph
import org.meshtastic.app.navigation.connectionsGraph
import org.meshtastic.app.navigation.contactsGraph
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.app.ui.connections.DeviceType
import org.meshtastic.app.ui.connections.ScannerViewModel
import org.meshtastic.app.ui.connections.components.ConnectionsNavIcon
import org.meshtastic.core.model.ConnectionState
import org.meshtastic.core.model.DeviceVersion
import org.meshtastic.core.model.MeshActivity
@@ -181,8 +181,6 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: ScannerVie
}
}
uIViewModel.AddNavigationTrackingEffect(navController)
VersionChecks(uIViewModel)
val alertDialogState by uIViewModel.currentAlert.collectAsStateWithLifecycle()
@@ -273,6 +271,7 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: ScannerVie
val receiveColor = capturedColorScheme.StatusBlue
LaunchedEffect(uIViewModel.meshActivity, capturedColorScheme) {
uIViewModel.meshActivity.collectLatest { activity ->
Logger.d { "MeshActivity received in UI: $activity" }
val newTargetColor =
when (activity) {
is MeshActivity.Send -> sendColor

View File

@@ -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 com.geeksville.mesh.ui.connections
package org.meshtastic.app.ui.connections
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.Arrangement
@@ -48,17 +48,17 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.DeviceListEntry
import com.geeksville.mesh.ui.connections.components.BLEDevices
import com.geeksville.mesh.ui.connections.components.ConnectingDeviceInfo
import com.geeksville.mesh.ui.connections.components.ConnectionsSegmentedBar
import com.geeksville.mesh.ui.connections.components.CurrentlyConnectedInfo
import com.geeksville.mesh.ui.connections.components.EmptyStateContent
import com.geeksville.mesh.ui.connections.components.NetworkDevices
import com.geeksville.mesh.ui.connections.components.UsbDevices
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import org.jetbrains.compose.resources.getString
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.app.model.DeviceListEntry
import org.meshtastic.app.ui.connections.components.BLEDevices
import org.meshtastic.app.ui.connections.components.ConnectingDeviceInfo
import org.meshtastic.app.ui.connections.components.ConnectionsSegmentedBar
import org.meshtastic.app.ui.connections.components.CurrentlyConnectedInfo
import org.meshtastic.app.ui.connections.components.EmptyStateContent
import org.meshtastic.app.ui.connections.components.NetworkDevices
import org.meshtastic.app.ui.connections.components.UsbDevices
import org.meshtastic.core.model.ConnectionState
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes

View File

@@ -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 com.geeksville.mesh.ui.connections
package org.meshtastic.app.ui.connections
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel

View File

@@ -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 com.geeksville.mesh.ui.connections
package org.meshtastic.app.ui.connections
/** Represent the different ways a device can connect to the phone. */
enum class DeviceType {

View File

@@ -14,15 +14,12 @@
* 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 com.geeksville.mesh.ui.connections
package org.meshtastic.app.ui.connections
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import co.touchlab.kermit.Logger
import co.touchlab.kermit.Severity
import com.geeksville.mesh.domain.usecase.GetDiscoveredDevicesUseCase
import com.geeksville.mesh.model.DeviceListEntry
import com.geeksville.mesh.repository.usb.UsbRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -34,6 +31,9 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.meshtastic.app.domain.usecase.GetDiscoveredDevicesUseCase
import org.meshtastic.app.model.DeviceListEntry
import org.meshtastic.app.repository.usb.UsbRepository
import org.meshtastic.core.ble.BluetoothRepository
import org.meshtastic.core.datastore.RecentAddressesDataSource
import org.meshtastic.core.datastore.model.RecentAddress

View File

@@ -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 com.geeksville.mesh.ui.connections.components
package org.meshtastic.app.ui.connections.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
@@ -30,11 +30,11 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.DeviceListEntry
import com.geeksville.mesh.ui.connections.ScannerViewModel
import no.nordicsemi.android.common.scanner.rememberFilterState
import no.nordicsemi.android.common.scanner.view.ScannerView
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.app.model.DeviceListEntry
import org.meshtastic.app.ui.connections.ScannerViewModel
import org.meshtastic.core.ble.MeshtasticBleConstants.BLE_NAME_PATTERN
import org.meshtastic.core.ble.MeshtasticBleConstants.SERVICE_UUID
import org.meshtastic.core.model.ConnectionState

View File

@@ -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 com.geeksville.mesh.ui.connections.components
package org.meshtastic.app.ui.connections.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column

View File

@@ -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 com.geeksville.mesh.ui.connections.components
package org.meshtastic.app.ui.connections.components
import androidx.compose.animation.Crossfade
import androidx.compose.material.icons.Icons
@@ -37,7 +37,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.geeksville.mesh.ui.connections.DeviceType
import org.meshtastic.app.ui.connections.DeviceType
import org.meshtastic.core.model.ConnectionState
import org.meshtastic.core.ui.icon.Device
import org.meshtastic.core.ui.icon.MeshtasticIcons

View File

@@ -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 com.geeksville.mesh.ui.connections.components
package org.meshtastic.app.ui.connections.components
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Bluetooth
@@ -30,9 +30,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import com.geeksville.mesh.ui.connections.DeviceType
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.app.ui.connections.DeviceType
import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.bluetooth
import org.meshtastic.core.resources.network

View File

@@ -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 com.geeksville.mesh.ui.connections.components
package org.meshtastic.app.ui.connections.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -40,13 +40,13 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import co.touchlab.kermit.Logger
import com.geeksville.mesh.model.DeviceListEntry
import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeout
import no.nordicsemi.android.common.ui.view.RssiIcon
import no.nordicsemi.kotlin.ble.client.exception.OperationFailedException
import no.nordicsemi.kotlin.ble.client.exception.PeripheralNotConnectedException
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.app.model.DeviceListEntry
import org.meshtastic.core.model.Node
import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.disconnect

View File

@@ -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 com.geeksville.mesh.ui.connections.components
package org.meshtastic.app.ui.connections.components
import androidx.compose.foundation.Indication
import androidx.compose.foundation.LocalIndication
@@ -52,10 +52,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.model.DeviceListEntry
import kotlinx.coroutines.delay
import no.nordicsemi.android.common.ui.view.RssiIcon
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.app.model.DeviceListEntry
import org.meshtastic.core.model.ConnectionState
import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.add

View File

@@ -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 com.geeksville.mesh.ui.connections.components
package org.meshtastic.app.ui.connections.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -27,7 +27,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.model.DeviceListEntry
import org.meshtastic.app.model.DeviceListEntry
import org.meshtastic.core.model.ConnectionState
@Composable

View File

@@ -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 com.geeksville.mesh.ui.connections.components
package org.meshtastic.app.ui.connections.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column

View File

@@ -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 com.geeksville.mesh.ui.connections.components
package org.meshtastic.app.ui.connections.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -47,11 +47,11 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.model.DeviceListEntry
import com.geeksville.mesh.repository.network.NetworkRepository
import com.geeksville.mesh.ui.connections.ScannerViewModel
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.app.model.DeviceListEntry
import org.meshtastic.app.repository.network.NetworkRepository
import org.meshtastic.app.ui.connections.ScannerViewModel
import org.meshtastic.core.common.util.isValidAddress
import org.meshtastic.core.model.ConnectionState
import org.meshtastic.core.resources.Res

View File

@@ -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 com.geeksville.mesh.ui.connections.components
package org.meshtastic.app.ui.connections.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@@ -24,9 +24,9 @@ import androidx.compose.material.icons.rounded.UsbOff
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.model.DeviceListEntry
import com.geeksville.mesh.ui.connections.ScannerViewModel
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.app.model.DeviceListEntry
import org.meshtastic.app.ui.connections.ScannerViewModel
import org.meshtastic.core.model.ConnectionState
import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.no_usb_devices

Some files were not shown because too many files have changed in this diff Show More