From cffbd0880690bfff94d7f78a85dbaa8eb8dc6ce0 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:06:50 -0600 Subject: [PATCH] =?UTF-8?q?refactor:=20migrate=20core=20modules=20to=20Kot?= =?UTF-8?q?lin=20Multiplatform=20and=20consolidat=E2=80=A6=20(#4735)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> --- app/build.gradle.kts | 19 +++- app/detekt-baseline.xml | 69 +------------ .../org/meshtastic/app}/TestRunner.kt | 10 +- .../filter/MessageFilterIntegrationTest.kt | 4 +- .../app/analytics}/FdroidPlatformAnalytics.kt | 28 ++---- .../meshtastic/app}/di/FDroidNetworkModule.kt | 11 +-- .../app}/di/FdroidPlatformAnalyticsModule.kt | 9 +- .../app/analytics}/GooglePlatformAnalytics.kt | 46 +++------ .../meshtastic/app}/di/GoogleNetworkModule.kt | 11 ++- .../app}/di/GooglePlatformAnalyticsModule.kt | 9 +- app/src/main/AndroidManifest.xml | 16 ++-- .../org/meshtastic/app}/ApplicationModule.kt | 14 +-- .../org/meshtastic/app}/MainActivity.kt | 6 +- .../org/meshtastic/app}/MeshServiceClient.kt | 6 +- .../meshtastic/app}/MeshUtilApplication.kt | 8 +- .../org/meshtastic/app}/di/AppModule.kt | 3 +- .../org/meshtastic/app}/di/DataModule.kt | 5 +- .../org/meshtastic/app/di/DataSourceModule.kt | 47 +++++++++ .../org/meshtastic/app}/di/DataStoreModule.kt | 2 +- .../org/meshtastic/app}/di/DatabaseModule.kt | 2 +- .../org/meshtastic/app/di/NetworkModule.kt | 96 +++++++++++++++++++ .../app}/di/NodeDataSourceModule.kt | 5 +- .../org/meshtastic/app}/di/PrefsModule.kt | 14 ++- .../meshtastic/app}/di/RepositoryModule.kt | 8 +- .../org/meshtastic/app}/di/UseCaseModule.kt | 2 +- .../usecase/GetDiscoveredDevicesUseCase.kt | 12 +-- .../meshtastic/app}/model/DeviceListEntry.kt | 2 +- .../org/meshtastic/app}/model/UIViewModel.kt | 22 +---- .../app}/navigation/ChannelsNavigation.kt | 7 +- .../app}/navigation/ConnectionsNavigation.kt | 7 +- .../app}/navigation/ContactsNavigation.kt | 4 +- .../app}/navigation/FirmwareNavigation.kt | 5 +- .../app}/navigation/MapNavigation.kt | 5 +- .../app}/navigation/NodesNavigation.kt | 4 +- .../app}/navigation/SettingsNavigation.kt | 2 +- .../repository/network/ConnectivityManager.kt | 46 ++++----- .../repository/network/NetworkRepository.kt | 2 +- .../network/NetworkRepositoryModule.kt | 15 ++- .../app}/repository/network/NsdManager.kt | 2 +- .../radio/AndroidRadioInterfaceService.kt | 59 ++++-------- .../app}/repository/radio/IRadioInterface.kt | 5 +- .../app}/repository/radio/InterfaceFactory.kt | 2 +- .../repository/radio/InterfaceFactorySpi.kt | 15 ++- .../app}/repository/radio/InterfaceMapKey.kt | 2 +- .../app}/repository/radio/InterfaceSpec.kt | 11 +-- .../radio/MeshtasticRadioProfile.kt | 2 +- .../radio/MeshtasticRadioServiceImpl.kt | 2 +- .../app}/repository/radio/MockInterface.kt | 2 +- .../repository/radio/MockInterfaceFactory.kt | 11 +-- .../repository/radio/MockInterfaceSpec.kt | 17 +--- .../app}/repository/radio/NopInterface.kt | 10 +- .../repository/radio/NopInterfaceFactory.kt | 11 +-- .../app}/repository/radio/NopInterfaceSpec.kt | 17 +--- .../repository/radio/NordicBleInterface.kt | 2 +- .../radio/NordicBleInterfaceFactory.kt | 5 +- .../radio/NordicBleInterfaceSpec.kt | 2 +- .../repository/radio/RadioRepositoryModule.kt | 2 +- .../app}/repository/radio/SerialInterface.kt | 8 +- .../radio/SerialInterfaceFactory.kt | 11 +-- .../repository/radio/SerialInterfaceSpec.kt | 4 +- .../app}/repository/radio/StreamInterface.kt | 2 +- .../app}/repository/radio/TCPInterface.kt | 4 +- .../repository/radio/TCPInterfaceFactory.kt | 11 +-- .../app}/repository/radio/TCPInterfaceSpec.kt | 17 +--- .../app}/repository/usb/ProbeTableProvider.kt | 24 ++--- .../meshtastic/app}/repository/usb/README.md | 0 .../app}/repository/usb/SerialConnection.kt | 19 ++-- .../repository/usb/SerialConnectionImpl.kt | 2 +- .../usb/SerialConnectionListener.kt | 28 ++---- .../repository/usb/UsbBroadcastReceiver.kt | 2 +- .../app}/repository/usb/UsbManager.kt | 2 +- .../app}/repository/usb/UsbRepository.kt | 2 +- .../repository/usb/UsbRepositoryModule.kt | 13 +-- .../app}/service/AndroidAppWidgetUpdater.kt | 4 +- .../service/AndroidMeshLocationManager.kt | 4 +- .../app}/service/AndroidMeshWorkerManager.kt | 2 +- .../app}/service/BootCompleteReceiver.kt | 5 +- .../org/meshtastic/app}/service/Constants.kt | 2 +- .../app}/service/MarkAsReadReceiver.kt | 2 +- .../meshtastic/app}/service/MeshService.kt | 6 +- .../service/MeshServiceNotificationsImpl.kt | 16 ++-- .../app}/service/MeshServiceStarter.kt | 6 +- .../app}/service/ReactionReceiver.kt | 4 +- .../meshtastic/app}/service/ReplyReceiver.kt | 4 +- .../app}/service/ServiceBroadcasts.kt | 2 +- .../org/meshtastic/app}/ui/Main.kt | 31 +++--- .../app}/ui/connections/ConnectionsScreen.kt | 18 ++-- .../ui/connections/ConnectionsViewModel.kt | 2 +- .../app}/ui/connections/DeviceType.kt | 2 +- .../app}/ui/connections/ScannerViewModel.kt | 8 +- .../ui/connections/components/BLEDevices.kt | 6 +- .../components/ConnectingDeviceInfo.kt | 2 +- .../components/ConnectionsNavIcon.kt | 4 +- .../components/ConnectionsSegmentedBar.kt | 4 +- .../components/CurrentlyConnectedInfo.kt | 4 +- .../connections/components/DeviceListItem.kt | 4 +- .../components/DeviceListSection.kt | 4 +- .../components/EmptyStateContent.kt | 2 +- .../connections/components/NetworkDevices.kt | 8 +- .../ui/connections/components/UsbDevices.kt | 6 +- .../app}/ui/node/AdaptiveNodeListScreen.kt | 2 +- .../org/meshtastic/app}/ui/sharing/Channel.kt | 2 +- .../app}/ui/sharing/ChannelViewModel.kt | 6 +- .../app}/widget/LocalStatsWidget.kt | 10 +- .../app}/widget/LocalStatsWidgetReceiver.kt | 2 +- .../app}/widget/LocalStatsWidgetState.kt | 2 +- .../app}/widget/RefreshLocalStatsAction.kt | 2 +- .../app}/worker/MeshLogCleanupWorker.kt | 2 +- .../app}/worker/ServiceKeepAliveWorker.kt | 8 +- .../meshtastic/app}/MeshTestApplication.kt | 2 +- .../radio/NordicBleInterfaceRetryTest.kt | 2 +- .../radio/NordicBleInterfaceTest.kt | 2 +- .../repository/radio/StreamInterfaceTest.kt | 2 +- .../app}/repository/radio/TCPInterfaceTest.kt | 4 +- .../org/meshtastic/app}/service/Fakes.kt | 2 +- .../app}/service/ServiceBroadcastsTest.kt | 4 +- .../org/meshtastic/app}/ui/UIUnitTest.kt | 2 +- .../app}/ui/metrics/EnvironmentMetricsTest.kt | 2 +- app/src/test/resources/robolectric.properties | 2 +- core/analytics/README.md | 35 ------- core/analytics/build.gradle.kts | 56 ----------- core/common/build.gradle.kts | 1 + core/data/build.gradle.kts | 77 +++++++++------ core/data/detekt-baseline.xml | 6 +- .../BootloaderOtaQuirksJsonDataSourceImpl.kt} | 8 +- .../DeviceHardwareJsonDataSourceImpl.kt} | 8 +- .../FirmwareReleaseJsonDataSourceImpl.kt} | 8 +- .../repository/LocationRepositoryImpl.kt} | 41 ++++---- .../BootloaderOtaQuirksJsonDataSource.kt | 23 +++++ .../DeviceHardwareJsonDataSource.kt | 23 +++++ .../DeviceHardwareLocalDataSource.kt | 0 .../FirmwareReleaseJsonDataSource.kt | 23 +++++ .../FirmwareReleaseLocalDataSource.kt | 0 .../data/datasource/NodeInfoReadDataSource.kt | 3 +- .../datasource/NodeInfoWriteDataSource.kt | 0 .../SwitchingNodeInfoReadDataSource.kt | 0 .../SwitchingNodeInfoWriteDataSource.kt | 0 .../core/data/manager/CommandSenderImpl.kt | 19 ++-- .../manager/FromRadioPacketHandlerImpl.kt | 0 .../core/data/manager/HistoryManagerImpl.kt | 0 .../data/manager/MeshActionHandlerImpl.kt | 4 +- .../data/manager/MeshConfigFlowManagerImpl.kt | 4 +- .../data/manager/MeshConfigHandlerImpl.kt | 0 .../data/manager/MeshConnectionManagerImpl.kt | 4 +- .../core/data/manager/MeshDataHandlerImpl.kt | 53 ++++++---- .../data/manager/MeshMessageProcessorImpl.kt | 76 +++++++++------ .../core/data/manager/MeshRouterImpl.kt | 0 .../core/data/manager/MessageFilterImpl.kt | 3 +- .../core/data/manager/MqttManagerImpl.kt | 0 .../data/manager/NeighborInfoHandlerImpl.kt | 0 .../core/data/manager/NodeManagerImpl.kt | 84 +++++++++------- .../core/data/manager/PacketHandlerImpl.kt | 82 ++++++++++------ .../data/manager/TracerouteHandlerImpl.kt | 0 .../DeviceHardwareRepositoryImpl.kt | 0 .../repository/FirmwareReleaseRepository.kt | 0 .../data/repository/MeshLogRepositoryImpl.kt | 0 .../data/repository/NodeRepositoryImpl.kt | 0 .../data/repository/PacketRepositoryImpl.kt | 0 .../repository/QuickChatActionRepository.kt | 3 +- .../repository/RadioConfigRepositoryImpl.kt | 0 .../TracerouteSnapshotRepository.kt | 0 .../data/manager/CommandSenderHopLimitTest.kt | 0 .../data/manager/CommandSenderImplTest.kt | 0 .../manager/FromRadioPacketHandlerImplTest.kt | 0 .../data/manager/HistoryManagerImplTest.kt | 0 .../manager/MeshConnectionManagerImplTest.kt | 2 +- .../core/data/manager/MeshDataHandlerTest.kt | 2 +- .../data/manager/MessageFilterImplTest.kt | 0 .../core/data/manager/NodeManagerImplTest.kt | 0 .../data/manager/PacketHandlerImplTest.kt | 0 .../DeviceHardwareRepositoryTest.kt | 0 .../data/repository/MeshLogRepositoryTest.kt | 0 .../data/repository/NodeRepositoryTest.kt | 0 .../core/data/di/GoogleDataModule.kt | 42 -------- core/database/detekt-baseline.xml | 5 +- core/datastore/build.gradle.kts | 10 +- core/di/build.gradle.kts | 38 +++----- .../core/di/CoroutineDispatchers.kt | 3 +- .../meshtastic/core/di/ProcessLifecycle.kt | 3 +- core/domain/build.gradle.kts | 54 +++++++---- .../usecase/settings/AdminActionsUseCase.kt | 0 .../settings/CleanNodeDatabaseUseCase.kt | 0 .../usecase/settings/ExportDataUseCase.kt | 28 +++--- .../usecase/settings/ExportProfileUseCase.kt | 11 ++- .../settings/ExportSecurityConfigUseCase.kt | 41 ++++---- .../usecase/settings/ImportProfileUseCase.kt | 10 +- .../usecase/settings/InstallProfileUseCase.kt | 0 .../usecase/settings/IsOtaCapableUseCase.kt | 0 .../usecase/settings/MeshLocationUseCase.kt | 0 .../settings/ProcessRadioResponseUseCase.kt | 0 .../usecase/settings/RadioConfigUseCase.kt | 0 .../settings/SetAppIntroCompletedUseCase.kt | 0 .../settings/SetDatabaseCacheLimitUseCase.kt | 0 .../settings/SetMeshLogSettingsUseCase.kt | 0 .../settings/SetProvideLocationUseCase.kt | 0 .../usecase/settings/SetThemeUseCase.kt | 0 .../settings/ToggleAnalyticsUseCase.kt | 0 .../ToggleHomoglyphEncodingUseCase.kt | 0 .../core/domain/FakeRadioController.kt | 0 .../domain/usecase/SendMessageUseCaseTest.kt | 14 +-- .../settings/AdminActionsUseCaseTest.kt | 8 +- .../settings/CleanNodeDatabaseUseCaseTest.kt | 8 +- .../usecase/settings/ExportDataUseCaseTest.kt | 28 +++--- .../settings/ExportProfileUseCaseTest.kt | 18 ++-- .../ExportSecurityConfigUseCaseTest.kt | 35 ++++--- .../settings/ImportProfileUseCaseTest.kt | 20 ++-- .../settings/InstallProfileUseCaseTest.kt | 6 +- .../settings/IsOtaCapableUseCaseTest.kt | 10 +- .../settings/MeshLocationUseCaseTest.kt | 6 +- .../ProcessRadioResponseUseCaseTest.kt | 10 +- .../settings/RadioConfigUseCaseTest.kt | 8 +- .../SetAppIntroCompletedUseCaseTest.kt | 6 +- .../SetDatabaseCacheLimitUseCaseTest.kt | 6 +- .../settings/SetMeshLogSettingsUseCaseTest.kt | 6 +- .../settings/SetProvideLocationUseCaseTest.kt | 6 +- .../usecase/settings/SetThemeUseCaseTest.kt | 6 +- .../settings/ToggleAnalyticsUseCaseTest.kt | 6 +- .../ToggleHomoglyphEncodingUseCaseTest.kt | 6 +- core/model/detekt-baseline.xml | 5 - core/network/build.gradle.kts | 66 ++++++++----- .../network/repository/MQTTRepositoryImpl.kt} | 10 +- .../repository/TrustAllX509TrustManager.kt | 0 .../network/DeviceHardwareRemoteDataSource.kt | 0 .../FirmwareReleaseRemoteDataSource.kt | 0 .../core/network/repository/MQTTRepository.kt | 41 ++++++++ .../core/network/service/ApiService.kt | 3 +- core/network/src/main/AndroidManifest.xml | 4 - .../core/network/di/NetworkModule.kt | 81 ---------------- core/prefs/build.gradle.kts | 42 +++++--- .../core/prefs/filter/FilterPrefsTest.kt | 0 .../prefs/analytics/AnalyticsPrefsImpl.kt | 0 .../meshtastic/core/prefs/di/Qualifiers.kt | 67 +++++++++++++ .../core/prefs/emoji/CustomEmojiPrefsImpl.kt | 0 .../core/prefs/filter/FilterPrefsImpl.kt | 0 .../prefs/homoglyph/HomoglyphPrefsImpl.kt | 0 .../core/prefs/map/MapConsentPrefsImpl.kt | 0 .../meshtastic/core/prefs/map/MapPrefsImpl.kt | 0 .../prefs/map/MapTileProviderPrefsImpl.kt | 0 .../core/prefs/mesh/MeshPrefsImpl.kt | 0 .../core/prefs/meshlog/MeshLogPrefsImpl.kt | 0 .../core/prefs/radio/RadioPrefsImpl.kt | 0 .../meshtastic/core/prefs/ui/UiPrefsImpl.kt | 0 .../meshtastic/core/repository/Location.kt | 20 ++++ .../meshtastic/core/repository}/DataPair.kt | 5 +- .../core/repository/LocationRepository.kt | 31 ++++++ .../core/repository}/PlatformAnalytics.kt | 16 +--- .../service/AndroidRadioControllerImpl.kt | 2 +- feature/map/build.gradle.kts | 3 + .../meshtastic/feature/map/MapViewModel.kt | 6 +- .../CustomTileProviderManagerSheet.kt | 2 +- .../map}/model/CustomTileProviderConfig.kt | 2 +- .../feature/map}/prefs/di/GoogleMapsModule.kt | 14 ++- .../feature/map}/prefs/map/GoogleMapsPrefs.kt | 4 +- .../CustomTileProviderRepository.kt | 4 +- .../feature/map/MapViewModelTest.kt | 6 +- feature/messaging/build.gradle.kts | 1 - .../meshtastic/feature/messaging/Message.kt | 5 +- feature/node/detekt-baseline.xml | 4 - feature/settings/detekt-baseline.xml | 11 --- .../feature/settings/SettingsViewModel.kt | 12 ++- .../settings/radio/RadioConfigViewModel.kt | 11 ++- .../radio/RadioConfigViewModelTest.kt | 2 +- gradle/libs.versions.toml | 1 + .../meshserviceexample/MainActivity.kt | 2 +- settings.gradle.kts | 1 - 265 files changed, 1383 insertions(+), 1340 deletions(-) rename app/src/androidTest/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/TestRunner.kt (83%) rename app/src/androidTest/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/filter/MessageFilterIntegrationTest.kt (92%) rename {core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform => app/src/fdroid/kotlin/org/meshtastic/app/analytics}/FdroidPlatformAnalytics.kt (67%) rename {core/network/src/fdroid/kotlin/org/meshtastic/core/network => app/src/fdroid/kotlin/org/meshtastic/app}/di/FDroidNetworkModule.kt (86%) rename {core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics => app/src/fdroid/kotlin/org/meshtastic/app}/di/FdroidPlatformAnalyticsModule.kt (83%) rename {core/analytics/src/google/kotlin/org/meshtastic/core/analytics/platform => app/src/google/kotlin/org/meshtastic/app/analytics}/GooglePlatformAnalytics.kt (88%) rename {core/network/src/google/kotlin/org/meshtastic/core/network => app/src/google/kotlin/org/meshtastic/app}/di/GoogleNetworkModule.kt (87%) rename {core/analytics/src/google/kotlin/org/meshtastic/core/analytics => app/src/google/kotlin/org/meshtastic/app}/di/GooglePlatformAnalyticsModule.kt (83%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ApplicationModule.kt (87%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/MainActivity.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/MeshServiceClient.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/MeshUtilApplication.kt (96%) rename {core/di/src/main/kotlin/org/meshtastic/core => app/src/main/kotlin/org/meshtastic/app}/di/AppModule.kt (94%) rename {core/data/src/main/kotlin/org/meshtastic/core/data => app/src/main/kotlin/org/meshtastic/app}/di/DataModule.kt (94%) create mode 100644 app/src/main/kotlin/org/meshtastic/app/di/DataSourceModule.kt rename {core/data/src/main/kotlin/org/meshtastic/core/data => app/src/main/kotlin/org/meshtastic/app}/di/DataStoreModule.kt (99%) rename {core/data/src/main/kotlin/org/meshtastic/core/data => app/src/main/kotlin/org/meshtastic/app}/di/DatabaseModule.kt (96%) create mode 100644 app/src/main/kotlin/org/meshtastic/app/di/NetworkModule.kt rename {core/data/src/main/kotlin/org/meshtastic/core/data => app/src/main/kotlin/org/meshtastic/app}/di/NodeDataSourceModule.kt (95%) rename {core/prefs/src/main/kotlin/org/meshtastic/core/prefs => app/src/main/kotlin/org/meshtastic/app}/di/PrefsModule.kt (93%) rename {core/data/src/main/kotlin/org/meshtastic/core/data => app/src/main/kotlin/org/meshtastic/app}/di/RepositoryModule.kt (95%) rename {core/data/src/main/kotlin/org/meshtastic/core/data => app/src/main/kotlin/org/meshtastic/app}/di/UseCaseModule.kt (97%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/domain/usecase/GetDiscoveredDevicesUseCase.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/model/DeviceListEntry.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/model/UIViewModel.kt (92%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/navigation/ChannelsNavigation.kt (94%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/navigation/ConnectionsNavigation.kt (95%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/navigation/ContactsNavigation.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/navigation/FirmwareNavigation.kt (93%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/navigation/MapNavigation.kt (95%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/navigation/NodesNavigation.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/navigation/SettingsNavigation.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/network/ConnectivityManager.kt (63%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/network/NetworkRepository.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/network/NetworkRepositoryModule.kt (78%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/network/NsdManager.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/AndroidRadioInterfaceService.kt (89%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/IRadioInterface.kt (92%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/InterfaceFactory.kt (97%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/InterfaceFactorySpi.kt (65%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/InterfaceMapKey.kt (95%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/InterfaceSpec.kt (82%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/MeshtasticRadioProfile.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/MeshtasticRadioServiceImpl.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/MockInterface.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/MockInterfaceFactory.kt (84%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/MockInterfaceSpec.kt (69%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/NopInterface.kt (88%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/NopInterfaceFactory.kt (84%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/NopInterfaceSpec.kt (65%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/NordicBleInterface.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/NordicBleInterfaceFactory.kt (90%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/NordicBleInterfaceSpec.kt (97%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/RadioRepositoryModule.kt (97%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/SerialInterface.kt (95%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/SerialInterfaceFactory.kt (84%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/SerialInterfaceSpec.kt (94%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/StreamInterface.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/TCPInterface.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/TCPInterfaceFactory.kt (84%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/TCPInterfaceSpec.kt (65%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/usb/ProbeTableProvider.kt (63%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/usb/README.md (100%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/usb/SerialConnection.kt (74%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/usb/SerialConnectionImpl.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/usb/SerialConnectionListener.kt (63%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/usb/UsbBroadcastReceiver.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/usb/UsbManager.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/usb/UsbRepository.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/usb/UsbRepositoryModule.kt (80%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/AndroidAppWidgetUpdater.kt (94%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/AndroidMeshLocationManager.kt (97%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/AndroidMeshWorkerManager.kt (97%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/BootCompleteReceiver.kt (94%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/Constants.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/MarkAsReadReceiver.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/MeshService.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/MeshServiceNotificationsImpl.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/MeshServiceStarter.kt (94%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/ReactionReceiver.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/ReplyReceiver.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/ServiceBroadcasts.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/Main.kt (97%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/ConnectionsScreen.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/ConnectionsViewModel.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/DeviceType.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/ScannerViewModel.kt (97%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/components/BLEDevices.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/components/ConnectingDeviceInfo.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/components/ConnectionsNavIcon.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/components/ConnectionsSegmentedBar.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/components/CurrentlyConnectedInfo.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/components/DeviceListItem.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/components/DeviceListSection.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/components/EmptyStateContent.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/components/NetworkDevices.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/connections/components/UsbDevices.kt (92%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/node/AdaptiveNodeListScreen.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/sharing/Channel.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/sharing/ChannelViewModel.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/widget/LocalStatsWidget.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/widget/LocalStatsWidgetReceiver.kt (96%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/widget/LocalStatsWidgetState.kt (99%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/widget/RefreshLocalStatsAction.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/worker/MeshLogCleanupWorker.kt (98%) rename app/src/main/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/worker/ServiceKeepAliveWorker.kt (95%) rename app/src/test/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/MeshTestApplication.kt (98%) rename app/src/test/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/NordicBleInterfaceRetryTest.kt (99%) rename app/src/test/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/NordicBleInterfaceTest.kt (99%) rename app/src/test/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/StreamInterfaceTest.kt (98%) rename app/src/test/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/repository/radio/TCPInterfaceTest.kt (95%) rename app/src/test/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/Fakes.kt (98%) rename app/src/test/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/service/ServiceBroadcastsTest.kt (96%) rename app/src/test/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/UIUnitTest.kt (98%) rename app/src/test/{java/com/geeksville/mesh => kotlin/org/meshtastic/app}/ui/metrics/EnvironmentMetricsTest.kt (98%) delete mode 100644 core/analytics/README.md delete mode 100644 core/analytics/build.gradle.kts rename core/data/src/{main/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt => androidMain/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSourceImpl.kt} (83%) rename core/data/src/{main/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt => androidMain/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSourceImpl.kt} (85%) rename core/data/src/{main/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt => androidMain/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSourceImpl.kt} (85%) rename core/data/src/{main/kotlin/org/meshtastic/core/data/repository/LocationRepository.kt => androidMain/kotlin/org/meshtastic/core/data/repository/LocationRepositoryImpl.kt} (77%) create mode 100644 core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt create mode 100644 core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareLocalDataSource.kt (100%) create mode 100644 core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseLocalDataSource.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/datasource/NodeInfoReadDataSource.kt (97%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/datasource/NodeInfoWriteDataSource.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoReadDataSource.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoWriteDataSource.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt (96%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/FromRadioPacketHandlerImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/HistoryManagerImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/MeshActionHandlerImpl.kt (99%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/MeshConfigFlowManagerImpl.kt (98%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/MeshConfigHandlerImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImpl.kt (99%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerImpl.kt (94%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/MeshMessageProcessorImpl.kt (81%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/MeshRouterImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/MessageFilterImpl.kt (95%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/MqttManagerImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/NeighborInfoHandlerImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/NodeManagerImpl.kt (82%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/PacketHandlerImpl.kt (70%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/manager/TracerouteHandlerImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepositoryImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/repository/NodeRepositoryImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/repository/PacketRepositoryImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/repository/QuickChatActionRepository.kt (97%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/repository/RadioConfigRepositoryImpl.kt (100%) rename core/data/src/{main => commonMain}/kotlin/org/meshtastic/core/data/repository/TracerouteSnapshotRepository.kt (100%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/manager/CommandSenderHopLimitTest.kt (100%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/manager/CommandSenderImplTest.kt (100%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/manager/FromRadioPacketHandlerImplTest.kt (100%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/manager/HistoryManagerImplTest.kt (100%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImplTest.kt (99%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerTest.kt (99%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/manager/MessageFilterImplTest.kt (100%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/manager/NodeManagerImplTest.kt (100%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/manager/PacketHandlerImplTest.kt (100%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepositoryTest.kt (100%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryTest.kt (100%) rename core/data/src/{test => commonTest}/kotlin/org/meshtastic/core/data/repository/NodeRepositoryTest.kt (100%) delete mode 100644 core/data/src/google/kotlin/org/meshtastic/core/data/di/GoogleDataModule.kt rename core/di/src/{main => commonMain}/kotlin/org/meshtastic/core/di/CoroutineDispatchers.kt (95%) rename core/di/src/{main => commonMain}/kotlin/org/meshtastic/core/di/ProcessLifecycle.kt (95%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCase.kt (85%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCase.kt (77%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCase.kt (53%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt (79%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCase.kt (100%) rename core/domain/src/{main => commonMain}/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCase.kt (100%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/FakeRadioController.kt (100%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/SendMessageUseCaseTest.kt (97%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCaseTest.kt (96%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCaseTest.kt (96%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCaseTest.kt (79%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCaseTest.kt (77%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCaseTest.kt (66%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCaseTest.kt (78%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCaseTest.kt (97%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCaseTest.kt (97%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCaseTest.kt (95%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt (96%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCaseTest.kt (98%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCaseTest.kt (95%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCaseTest.kt (95%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCaseTest.kt (97%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCaseTest.kt (94%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCaseTest.kt (95%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCaseTest.kt (96%) rename core/domain/src/{test => commonTest}/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCaseTest.kt (96%) rename core/network/src/{main/kotlin/org/meshtastic/core/network/repository/MQTTRepository.kt => androidMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt} (96%) rename core/network/src/{main => androidMain}/kotlin/org/meshtastic/core/network/repository/TrustAllX509TrustManager.kt (100%) rename core/network/src/{main => commonMain}/kotlin/org/meshtastic/core/network/DeviceHardwareRemoteDataSource.kt (100%) rename core/network/src/{main => commonMain}/kotlin/org/meshtastic/core/network/FirmwareReleaseRemoteDataSource.kt (100%) create mode 100644 core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepository.kt rename core/network/src/{main => commonMain}/kotlin/org/meshtastic/core/network/service/ApiService.kt (97%) delete mode 100644 core/network/src/main/AndroidManifest.xml delete mode 100644 core/network/src/main/kotlin/org/meshtastic/core/network/di/NetworkModule.kt rename core/prefs/src/{test => androidUnitTest}/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsTest.kt (100%) rename core/prefs/src/{main => commonMain}/kotlin/org/meshtastic/core/prefs/analytics/AnalyticsPrefsImpl.kt (100%) create mode 100644 core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/di/Qualifiers.kt rename core/prefs/src/{main => commonMain}/kotlin/org/meshtastic/core/prefs/emoji/CustomEmojiPrefsImpl.kt (100%) rename core/prefs/src/{main => commonMain}/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsImpl.kt (100%) rename core/prefs/src/{main => commonMain}/kotlin/org/meshtastic/core/prefs/homoglyph/HomoglyphPrefsImpl.kt (100%) rename core/prefs/src/{main => commonMain}/kotlin/org/meshtastic/core/prefs/map/MapConsentPrefsImpl.kt (100%) rename core/prefs/src/{main => commonMain}/kotlin/org/meshtastic/core/prefs/map/MapPrefsImpl.kt (100%) rename core/prefs/src/{main => commonMain}/kotlin/org/meshtastic/core/prefs/map/MapTileProviderPrefsImpl.kt (100%) rename core/prefs/src/{main => commonMain}/kotlin/org/meshtastic/core/prefs/mesh/MeshPrefsImpl.kt (100%) rename core/prefs/src/{main => commonMain}/kotlin/org/meshtastic/core/prefs/meshlog/MeshLogPrefsImpl.kt (100%) rename core/prefs/src/{main => commonMain}/kotlin/org/meshtastic/core/prefs/radio/RadioPrefsImpl.kt (100%) rename core/prefs/src/{main => commonMain}/kotlin/org/meshtastic/core/prefs/ui/UiPrefsImpl.kt (100%) create mode 100644 core/repository/src/androidMain/kotlin/org/meshtastic/core/repository/Location.kt rename core/{analytics/src/main/kotlin/org/meshtastic/core/analytics => repository/src/commonMain/kotlin/org/meshtastic/core/repository}/DataPair.kt (92%) create mode 100644 core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LocationRepository.kt rename core/{analytics/src/main/kotlin/org/meshtastic/core/analytics/platform => repository/src/commonMain/kotlin/org/meshtastic/core/repository}/PlatformAnalytics.kt (75%) rename {core/data/src/google/kotlin/org/meshtastic/core/data => feature/map/src/google/kotlin/org/meshtastic/feature/map}/model/CustomTileProviderConfig.kt (96%) rename {core/prefs/src/google/kotlin/org/meshtastic/core => feature/map/src/google/kotlin/org/meshtastic/feature/map}/prefs/di/GoogleMapsModule.kt (81%) rename {core/prefs/src/google/kotlin/org/meshtastic/core => feature/map/src/google/kotlin/org/meshtastic/feature/map}/prefs/map/GoogleMapsPrefs.kt (98%) rename {core/data/src/google/kotlin/org/meshtastic/core/data => feature/map/src/google/kotlin/org/meshtastic/feature/map}/repository/CustomTileProviderRepository.kt (97%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2a740864b..e0d08bbf7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -44,7 +44,7 @@ if (keystorePropertiesFile.exists()) { } configure { - namespace = configProperties.getProperty("APPLICATION_ID") + namespace = "org.meshtastic.app" signingConfigs { create("release") { @@ -150,7 +150,7 @@ configure { 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") } diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml index 801f6c2f2..0e08e976a 100644 --- a/app/detekt-baseline.xml +++ b/app/detekt-baseline.xml @@ -2,91 +2,26 @@ - CommentSpacing:Coroutines.kt$/// Wrap launch with an exception handler, FIXME, move into a utility lib - CyclomaticComplexMethod:BleError.kt$BleError.Companion$fun from(exception: Throwable): BleError - CyclomaticComplexMethod:MeshMessageProcessor.kt$MeshMessageProcessor$private fun processReceivedMeshPacket(packet: MeshPacket, myNodeNum: Int?) CyclomaticComplexMethod:SettingsNavigation.kt$@Suppress("LongMethod") fun NavGraphBuilder.settingsGraph(navController: NavHostController) - EmptyClassBlock:DebugLogFile.kt$BinaryLogFile${ } - EmptyFunctionBlock:NopInterface.kt$NopInterface${ } - EmptyFunctionBlock:TrustAllX509TrustManager.kt$TrustAllX509TrustManager${} - FinalNewline:Coroutines.kt$com.geeksville.mesh.concurrent.Coroutines.kt - FinalNewline:DateUtils.kt$com.geeksville.mesh.android.DateUtils.kt - FinalNewline:DebugLogFile.kt$com.geeksville.mesh.android.DebugLogFile.kt - FinalNewline:InterfaceId.kt$com.geeksville.mesh.repository.radio.InterfaceId.kt - FinalNewline:InterfaceSpec.kt$com.geeksville.mesh.repository.radio.InterfaceSpec.kt - FinalNewline:MockInterfaceFactory.kt$com.geeksville.mesh.repository.radio.MockInterfaceFactory.kt - FinalNewline:NopInterface.kt$com.geeksville.mesh.repository.radio.NopInterface.kt - FinalNewline:NopInterfaceFactory.kt$com.geeksville.mesh.repository.radio.NopInterfaceFactory.kt - FinalNewline:ProbeTableProvider.kt$com.geeksville.mesh.repository.usb.ProbeTableProvider.kt - FinalNewline:SerialConnection.kt$com.geeksville.mesh.repository.usb.SerialConnection.kt - FinalNewline:SerialConnectionListener.kt$com.geeksville.mesh.repository.usb.SerialConnectionListener.kt - FinalNewline:SerialInterfaceFactory.kt$com.geeksville.mesh.repository.radio.SerialInterfaceFactory.kt - FinalNewline:TCPInterfaceFactory.kt$com.geeksville.mesh.repository.radio.TCPInterfaceFactory.kt - FinalNewline:UsbRepositoryModule.kt$com.geeksville.mesh.repository.usb.UsbRepositoryModule.kt LongMethod:TCPInterface.kt$TCPInterface$private suspend fun startConnect() - MagicNumber:Contacts.kt$7 - MagicNumber:Contacts.kt$8 - MagicNumber:MQTTRepository.kt$MQTTRepository$512 MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$21972 MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$32809 MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$6790 MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$9114 MagicNumber:SerialConnectionImpl.kt$SerialConnectionImpl$115200 MagicNumber:SerialConnectionImpl.kt$SerialConnectionImpl$200 - MagicNumber:ServiceClient.kt$ServiceClient$500 MagicNumber:StreamInterface.kt$StreamInterface$0xff MagicNumber:StreamInterface.kt$StreamInterface$3 MagicNumber:StreamInterface.kt$StreamInterface$4 MagicNumber:StreamInterface.kt$StreamInterface$8 MagicNumber:TCPInterface.kt$TCPInterface$1000 - MagicNumber:UIState.kt$4 - MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found fromNum: ${fromNumCharacteristic?.uuid}, ${fromNumCharacteristic?.instanceId}" - MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found fromRadio: ${fromRadioCharacteristic?.uuid}, ${fromRadioCharacteristic?.instanceId}" - MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found logRadio: ${logRadioCharacteristic?.uuid}, ${logRadioCharacteristic?.instanceId}" - MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found toRadio: ${toRadioCharacteristic?.uuid}, ${toRadioCharacteristic?.instanceId}" - NewLineAtEndOfFile:Coroutines.kt$com.geeksville.mesh.concurrent.Coroutines.kt - NewLineAtEndOfFile:DateUtils.kt$com.geeksville.mesh.android.DateUtils.kt - NewLineAtEndOfFile:DebugLogFile.kt$com.geeksville.mesh.android.DebugLogFile.kt - NewLineAtEndOfFile:InterfaceId.kt$com.geeksville.mesh.repository.radio.InterfaceId.kt - NewLineAtEndOfFile:InterfaceSpec.kt$com.geeksville.mesh.repository.radio.InterfaceSpec.kt - NewLineAtEndOfFile:MockInterfaceFactory.kt$com.geeksville.mesh.repository.radio.MockInterfaceFactory.kt - NewLineAtEndOfFile:NopInterface.kt$com.geeksville.mesh.repository.radio.NopInterface.kt - NewLineAtEndOfFile:NopInterfaceFactory.kt$com.geeksville.mesh.repository.radio.NopInterfaceFactory.kt - NewLineAtEndOfFile:ProbeTableProvider.kt$com.geeksville.mesh.repository.usb.ProbeTableProvider.kt - NewLineAtEndOfFile:SerialConnection.kt$com.geeksville.mesh.repository.usb.SerialConnection.kt - NewLineAtEndOfFile:SerialConnectionListener.kt$com.geeksville.mesh.repository.usb.SerialConnectionListener.kt - NewLineAtEndOfFile:SerialInterfaceFactory.kt$com.geeksville.mesh.repository.radio.SerialInterfaceFactory.kt - NewLineAtEndOfFile:TCPInterfaceFactory.kt$com.geeksville.mesh.repository.radio.TCPInterfaceFactory.kt - NewLineAtEndOfFile:UsbRepositoryModule.kt$com.geeksville.mesh.repository.usb.UsbRepositoryModule.kt - NoBlankLineBeforeRbrace:DebugLogFile.kt$BinaryLogFile$ - NoBlankLineBeforeRbrace:NopInterface.kt$NopInterface$ - NoConsecutiveBlankLines:DebugLogFile.kt$ - NoEmptyClassBody:DebugLogFile.kt$BinaryLogFile${ } - NoSemicolons:DateUtils.kt$DateUtils$; - OptionalAbstractKeyword:SyncContinuation.kt$Continuation$abstract - RethrowCaughtException:SyncContinuation.kt$Continuation$throw ex - ReturnCount:MeshDataHandler.kt$MeshDataHandler$@Suppress("LongMethod") private fun handleStoreForwardPlusPlus(packet: MeshPacket) - ReturnCount:MeshDataHandler.kt$MeshDataHandler$private fun shouldBatteryNotificationShow(fromNum: Int, t: Telemetry, myNodeNum: Int): Boolean - SwallowedException:Exceptions.kt$ex: Throwable + MaxLineLength:DataSourceModule.kt$DataSourceModule$fun + ParameterListWrapping:DataSourceModule.kt$DataSourceModule$(impl: BootloaderOtaQuirksJsonDataSourceImpl) SwallowedException:NsdManager.kt$ex: IllegalArgumentException - SwallowedException:ServiceClient.kt$ServiceClient$ex: IllegalArgumentException SwallowedException:TCPInterface.kt$TCPInterface$ex: SocketTimeoutException - TooGenericExceptionCaught:Exceptions.kt$ex: Throwable - TooGenericExceptionCaught:MQTTRepository.kt$MQTTRepository$ex: Exception - TooGenericExceptionCaught:MeshDataHandler.kt$MeshDataHandler$e: Exception - TooGenericExceptionCaught:MeshService.kt$MeshService$ex: Exception TooGenericExceptionCaught:NordicBleInterface.kt$NordicBleInterface$e: Exception - TooGenericExceptionCaught:NordicBleInterface.kt$NordicBleInterface$t: Throwable - TooGenericExceptionCaught:RadioInterfaceService.kt$RadioInterfaceService$t: Throwable - TooGenericExceptionCaught:SyncContinuation.kt$Continuation$ex: Throwable TooGenericExceptionCaught:TCPInterface.kt$TCPInterface$ex: Throwable - TooGenericExceptionThrown:ServiceClient.kt$ServiceClient$throw Exception("Haven't called connect") - TooGenericExceptionThrown:ServiceClient.kt$ServiceClient$throw Exception("Service not bound") - TooGenericExceptionThrown:SyncContinuation.kt$SyncContinuation$throw Exception("SyncContinuation timeout") - TooGenericExceptionThrown:SyncContinuation.kt$SyncContinuation$throw Exception("This shouldn't happen") TooManyFunctions:NordicBleInterface.kt$NordicBleInterface : IRadioInterface - TooManyFunctions:RadioInterfaceService.kt$RadioInterfaceService - TooManyFunctions:UIState.kt$UIViewModel : ViewModel UtilityClassWithPublicConstructor:NetworkRepositoryModule.kt$NetworkRepositoryModule diff --git a/app/src/androidTest/java/com/geeksville/mesh/TestRunner.kt b/app/src/androidTest/kotlin/org/meshtastic/app/TestRunner.kt similarity index 83% rename from app/src/androidTest/java/com/geeksville/mesh/TestRunner.kt rename to app/src/androidTest/kotlin/org/meshtastic/app/TestRunner.kt index ab2d6714a..7a5f389ae 100644 --- a/app/src/androidTest/java/com/geeksville/mesh/TestRunner.kt +++ b/app/src/androidTest/kotlin/org/meshtastic/app/TestRunner.kt @@ -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 . */ - -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) } diff --git a/app/src/androidTest/java/com/geeksville/mesh/filter/MessageFilterIntegrationTest.kt b/app/src/androidTest/kotlin/org/meshtastic/app/filter/MessageFilterIntegrationTest.kt similarity index 92% rename from app/src/androidTest/java/com/geeksville/mesh/filter/MessageFilterIntegrationTest.kt rename to app/src/androidTest/kotlin/org/meshtastic/app/filter/MessageFilterIntegrationTest.kt index efa229881..a4c44e964 100644 --- a/app/src/androidTest/java/com/geeksville/mesh/filter/MessageFilterIntegrationTest.kt +++ b/app/src/androidTest/kotlin/org/meshtastic/app/filter/MessageFilterIntegrationTest.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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")) diff --git a/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform/FdroidPlatformAnalytics.kt b/app/src/fdroid/kotlin/org/meshtastic/app/analytics/FdroidPlatformAnalytics.kt similarity index 67% rename from core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform/FdroidPlatformAnalytics.kt rename to app/src/fdroid/kotlin/org/meshtastic/app/analytics/FdroidPlatformAnalytics.kt index a8b4532d1..69d9648d9 100644 --- a/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform/FdroidPlatformAnalytics.kt +++ b/app/src/fdroid/kotlin/org/meshtastic/app/analytics/FdroidPlatformAnalytics.kt @@ -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 . */ +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 diff --git a/core/network/src/fdroid/kotlin/org/meshtastic/core/network/di/FDroidNetworkModule.kt b/app/src/fdroid/kotlin/org/meshtastic/app/di/FDroidNetworkModule.kt similarity index 86% rename from core/network/src/fdroid/kotlin/org/meshtastic/core/network/di/FDroidNetworkModule.kt rename to app/src/fdroid/kotlin/org/meshtastic/app/di/FDroidNetworkModule.kt index 538400edc..a2716d1e0 100644 --- a/core/network/src/fdroid/kotlin/org/meshtastic/core/network/di/FDroidNetworkModule.kt +++ b/app/src/fdroid/kotlin/org/meshtastic/app/di/FDroidNetworkModule.kt @@ -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 . */ - -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) } }, diff --git a/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/di/FdroidPlatformAnalyticsModule.kt b/app/src/fdroid/kotlin/org/meshtastic/app/di/FdroidPlatformAnalyticsModule.kt similarity index 83% rename from core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/di/FdroidPlatformAnalyticsModule.kt rename to app/src/fdroid/kotlin/org/meshtastic/app/di/FdroidPlatformAnalyticsModule.kt index 9b0bd4492..47d3e7fd5 100644 --- a/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/di/FdroidPlatformAnalyticsModule.kt +++ b/app/src/fdroid/kotlin/org/meshtastic/app/di/FdroidPlatformAnalyticsModule.kt @@ -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 . */ - -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. */ diff --git a/core/analytics/src/google/kotlin/org/meshtastic/core/analytics/platform/GooglePlatformAnalytics.kt b/app/src/google/kotlin/org/meshtastic/app/analytics/GooglePlatformAnalytics.kt similarity index 88% rename from core/analytics/src/google/kotlin/org/meshtastic/core/analytics/platform/GooglePlatformAnalytics.kt rename to app/src/google/kotlin/org/meshtastic/app/analytics/GooglePlatformAnalytics.kt index c3133b8f4..30fa55730 100644 --- a/core/analytics/src/google/kotlin/org/meshtastic/core/analytics/platform/GooglePlatformAnalytics.kt +++ b/app/src/google/kotlin/org/meshtastic/app/analytics/GooglePlatformAnalytics.kt @@ -14,22 +14,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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) } diff --git a/core/network/src/google/kotlin/org/meshtastic/core/network/di/GoogleNetworkModule.kt b/app/src/google/kotlin/org/meshtastic/app/di/GoogleNetworkModule.kt similarity index 87% rename from core/network/src/google/kotlin/org/meshtastic/core/network/di/GoogleNetworkModule.kt rename to app/src/google/kotlin/org/meshtastic/app/di/GoogleNetworkModule.kt index abeef17a0..2a0894c45 100644 --- a/core/network/src/google/kotlin/org/meshtastic/core/network/di/GoogleNetworkModule.kt +++ b/app/src/google/kotlin/org/meshtastic/app/di/GoogleNetworkModule.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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) } }, diff --git a/core/analytics/src/google/kotlin/org/meshtastic/core/analytics/di/GooglePlatformAnalyticsModule.kt b/app/src/google/kotlin/org/meshtastic/app/di/GooglePlatformAnalyticsModule.kt similarity index 83% rename from core/analytics/src/google/kotlin/org/meshtastic/core/analytics/di/GooglePlatformAnalyticsModule.kt rename to app/src/google/kotlin/org/meshtastic/app/di/GooglePlatformAnalyticsModule.kt index 4281c2f0e..af63aab83 100644 --- a/core/analytics/src/google/kotlin/org/meshtastic/core/analytics/di/GooglePlatformAnalyticsModule.kt +++ b/app/src/google/kotlin/org/meshtastic/app/di/GooglePlatformAnalyticsModule.kt @@ -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 . */ - -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. */ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 383ee77f1..a19b6ff3c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -102,7 +102,7 @@ @@ -171,7 +171,7 @@ @@ -228,7 +228,7 @@ android:resource="@xml/device_filter" /> - @@ -252,12 +252,12 @@ android:path="com.geeksville.mesh" /> --> - - - + + + diff --git a/app/src/main/java/com/geeksville/mesh/ApplicationModule.kt b/app/src/main/kotlin/org/meshtastic/app/ApplicationModule.kt similarity index 87% rename from app/src/main/java/com/geeksville/mesh/ApplicationModule.kt rename to app/src/main/kotlin/org/meshtastic/app/ApplicationModule.kt index dd07d74e2..d609d38dd 100644 --- a/app/src/main/java/com/geeksville/mesh/ApplicationModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ApplicationModule.kt @@ -14,22 +14,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/kotlin/org/meshtastic/app/MainActivity.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/MainActivity.kt rename to app/src/main/kotlin/org/meshtastic/app/MainActivity.kt index 0fbe657ce..7de47507a 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/kotlin/org/meshtastic/app/MainActivity.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/MeshServiceClient.kt b/app/src/main/kotlin/org/meshtastic/app/MeshServiceClient.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/MeshServiceClient.kt rename to app/src/main/kotlin/org/meshtastic/app/MeshServiceClient.kt index 74fcea5bf..b683fd380 100644 --- a/app/src/main/java/com/geeksville/mesh/MeshServiceClient.kt +++ b/app/src/main/kotlin/org/meshtastic/app/MeshServiceClient.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt b/app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt rename to app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt index 6e1573f2d..daae4a159 100644 --- a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt +++ b/app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/core/di/src/main/kotlin/org/meshtastic/core/di/AppModule.kt b/app/src/main/kotlin/org/meshtastic/app/di/AppModule.kt similarity index 94% rename from core/di/src/main/kotlin/org/meshtastic/core/di/AppModule.kt rename to app/src/main/kotlin/org/meshtastic/app/di/AppModule.kt index 0dfe5764a..ec1efc74d 100644 --- a/core/di/src/main/kotlin/org/meshtastic/core/di/AppModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/di/AppModule.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/di/DataModule.kt b/app/src/main/kotlin/org/meshtastic/app/di/DataModule.kt similarity index 94% rename from core/data/src/main/kotlin/org/meshtastic/core/data/di/DataModule.kt rename to app/src/main/kotlin/org/meshtastic/app/di/DataModule.kt index 241f70218..e20f08582 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/di/DataModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/di/DataModule.kt @@ -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 . */ - -package org.meshtastic.core.data.di +package org.meshtastic.app.di import android.content.Context import android.location.LocationManager diff --git a/app/src/main/kotlin/org/meshtastic/app/di/DataSourceModule.kt b/app/src/main/kotlin/org/meshtastic/app/di/DataSourceModule.kt new file mode 100644 index 000000000..55a42e183 --- /dev/null +++ b/app/src/main/kotlin/org/meshtastic/app/di/DataSourceModule.kt @@ -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 . + */ +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 +} diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/di/DataStoreModule.kt b/app/src/main/kotlin/org/meshtastic/app/di/DataStoreModule.kt similarity index 99% rename from core/data/src/main/kotlin/org/meshtastic/core/data/di/DataStoreModule.kt rename to app/src/main/kotlin/org/meshtastic/app/di/DataStoreModule.kt index b34e2f52c..55611e300 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/di/DataStoreModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/di/DataStoreModule.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.core.data.di +package org.meshtastic.app.di import android.content.Context import androidx.datastore.core.DataStore diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/di/DatabaseModule.kt b/app/src/main/kotlin/org/meshtastic/app/di/DatabaseModule.kt similarity index 96% rename from core/data/src/main/kotlin/org/meshtastic/core/data/di/DatabaseModule.kt rename to app/src/main/kotlin/org/meshtastic/app/di/DatabaseModule.kt index 6660fb87d..059330e7a 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/di/DatabaseModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/di/DatabaseModule.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.core.data.di +package org.meshtastic.app.di import dagger.Binds import dagger.Module diff --git a/app/src/main/kotlin/org/meshtastic/app/di/NetworkModule.kt b/app/src/main/kotlin/org/meshtastic/app/di/NetworkModule.kt new file mode 100644 index 000000000..f3dabfe13 --- /dev/null +++ b/app/src/main/kotlin/org/meshtastic/app/di/NetworkModule.kt @@ -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 . + */ +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) } + } + } +} diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/di/NodeDataSourceModule.kt b/app/src/main/kotlin/org/meshtastic/app/di/NodeDataSourceModule.kt similarity index 95% rename from core/data/src/main/kotlin/org/meshtastic/core/data/di/NodeDataSourceModule.kt rename to app/src/main/kotlin/org/meshtastic/app/di/NodeDataSourceModule.kt index 42a50e980..54a91068d 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/di/NodeDataSourceModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/di/NodeDataSourceModule.kt @@ -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 . */ - -package org.meshtastic.core.data.di +package org.meshtastic.app.di import dagger.Binds import dagger.Module diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/di/PrefsModule.kt b/app/src/main/kotlin/org/meshtastic/app/di/PrefsModule.kt similarity index 93% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/di/PrefsModule.kt rename to app/src/main/kotlin/org/meshtastic/app/di/PrefsModule.kt index b1b8fbede..1d555b5b0 100644 --- a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/di/PrefsModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/di/PrefsModule.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/di/RepositoryModule.kt b/app/src/main/kotlin/org/meshtastic/app/di/RepositoryModule.kt similarity index 95% rename from core/data/src/main/kotlin/org/meshtastic/core/data/di/RepositoryModule.kt rename to app/src/main/kotlin/org/meshtastic/app/di/RepositoryModule.kt index 5c48a3745..98c19f5bc 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/di/RepositoryModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/di/RepositoryModule.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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( diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/di/UseCaseModule.kt b/app/src/main/kotlin/org/meshtastic/app/di/UseCaseModule.kt similarity index 97% rename from core/data/src/main/kotlin/org/meshtastic/core/data/di/UseCaseModule.kt rename to app/src/main/kotlin/org/meshtastic/app/di/UseCaseModule.kt index 8093d73e9..f0b078cea 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/di/UseCaseModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/di/UseCaseModule.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.core.data.di +package org.meshtastic.app.di import dagger.Module import dagger.Provides diff --git a/app/src/main/java/com/geeksville/mesh/domain/usecase/GetDiscoveredDevicesUseCase.kt b/app/src/main/kotlin/org/meshtastic/app/domain/usecase/GetDiscoveredDevicesUseCase.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/domain/usecase/GetDiscoveredDevicesUseCase.kt rename to app/src/main/kotlin/org/meshtastic/app/domain/usecase/GetDiscoveredDevicesUseCase.kt index 4b7a25c50..200294e16 100644 --- a/app/src/main/java/com/geeksville/mesh/domain/usecase/GetDiscoveredDevicesUseCase.kt +++ b/app/src/main/kotlin/org/meshtastic/app/domain/usecase/GetDiscoveredDevicesUseCase.kt @@ -14,19 +14,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/model/DeviceListEntry.kt b/app/src/main/kotlin/org/meshtastic/app/model/DeviceListEntry.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/model/DeviceListEntry.kt rename to app/src/main/kotlin/org/meshtastic/app/model/DeviceListEntry.kt index d66d6fff0..8d92cd7a8 100644 --- a/app/src/main/java/com/geeksville/mesh/model/DeviceListEntry.kt +++ b/app/src/main/kotlin/org/meshtastic/app/model/DeviceListEntry.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.model +package org.meshtastic.app.model import android.hardware.usb.UsbManager import com.hoho.android.usbserial.driver.UsbSerialDriver diff --git a/app/src/main/java/com/geeksville/mesh/model/UIViewModel.kt b/app/src/main/kotlin/org/meshtastic/app/model/UIViewModel.kt similarity index 92% rename from app/src/main/java/com/geeksville/mesh/model/UIViewModel.kt rename to app/src/main/kotlin/org/meshtastic/app/model/UIViewModel.kt index 77a6cde1f..54b2f6f2a 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIViewModel.kt +++ b/app/src/main/kotlin/org/meshtastic/app/model/UIViewModel.kt @@ -14,21 +14,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 = - radioInterfaceService.meshActivity.shareIn(viewModelScope, SharingStarted.Eagerly, 0) + /** Emits events for mesh network send/receive activity. */ + val meshActivity: Flow = radioInterfaceService.meshActivity private val _scrollToTopEventFlow = MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) @@ -276,9 +265,4 @@ constructor( fun onAppIntroCompleted() { uiPreferencesDataSource.setAppIntroCompleted(true) } - - @Composable - fun AddNavigationTrackingEffect(navController: NavHostController) { - analytics.AddNavigationTrackingEffect(navController) - } } diff --git a/app/src/main/java/com/geeksville/mesh/navigation/ChannelsNavigation.kt b/app/src/main/kotlin/org/meshtastic/app/navigation/ChannelsNavigation.kt similarity index 94% rename from app/src/main/java/com/geeksville/mesh/navigation/ChannelsNavigation.kt rename to app/src/main/kotlin/org/meshtastic/app/navigation/ChannelsNavigation.kt index ccf513922..819d72e13 100644 --- a/app/src/main/java/com/geeksville/mesh/navigation/ChannelsNavigation.kt +++ b/app/src/main/kotlin/org/meshtastic/app/navigation/ChannelsNavigation.kt @@ -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 . */ - -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 diff --git a/app/src/main/java/com/geeksville/mesh/navigation/ConnectionsNavigation.kt b/app/src/main/kotlin/org/meshtastic/app/navigation/ConnectionsNavigation.kt similarity index 95% rename from app/src/main/java/com/geeksville/mesh/navigation/ConnectionsNavigation.kt rename to app/src/main/kotlin/org/meshtastic/app/navigation/ConnectionsNavigation.kt index 8c94d688e..4ece8d6a5 100644 --- a/app/src/main/java/com/geeksville/mesh/navigation/ConnectionsNavigation.kt +++ b/app/src/main/kotlin/org/meshtastic/app/navigation/ConnectionsNavigation.kt @@ -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 . */ - -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 diff --git a/app/src/main/java/com/geeksville/mesh/navigation/ContactsNavigation.kt b/app/src/main/kotlin/org/meshtastic/app/navigation/ContactsNavigation.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/navigation/ContactsNavigation.kt rename to app/src/main/kotlin/org/meshtastic/app/navigation/ContactsNavigation.kt index aaf47dde6..9caec2f08 100644 --- a/app/src/main/java/com/geeksville/mesh/navigation/ContactsNavigation.kt +++ b/app/src/main/kotlin/org/meshtastic/app/navigation/ContactsNavigation.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/navigation/FirmwareNavigation.kt b/app/src/main/kotlin/org/meshtastic/app/navigation/FirmwareNavigation.kt similarity index 93% rename from app/src/main/java/com/geeksville/mesh/navigation/FirmwareNavigation.kt rename to app/src/main/kotlin/org/meshtastic/app/navigation/FirmwareNavigation.kt index 40ec2a4bc..88439d6c8 100644 --- a/app/src/main/java/com/geeksville/mesh/navigation/FirmwareNavigation.kt +++ b/app/src/main/kotlin/org/meshtastic/app/navigation/FirmwareNavigation.kt @@ -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 . */ - -package com.geeksville.mesh.navigation +package org.meshtastic.app.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder diff --git a/app/src/main/java/com/geeksville/mesh/navigation/MapNavigation.kt b/app/src/main/kotlin/org/meshtastic/app/navigation/MapNavigation.kt similarity index 95% rename from app/src/main/java/com/geeksville/mesh/navigation/MapNavigation.kt rename to app/src/main/kotlin/org/meshtastic/app/navigation/MapNavigation.kt index 5de1c6933..da766bd06 100644 --- a/app/src/main/java/com/geeksville/mesh/navigation/MapNavigation.kt +++ b/app/src/main/kotlin/org/meshtastic/app/navigation/MapNavigation.kt @@ -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 . */ - -package com.geeksville.mesh.navigation +package org.meshtastic.app.navigation import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController diff --git a/app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt b/app/src/main/kotlin/org/meshtastic/app/navigation/NodesNavigation.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt rename to app/src/main/kotlin/org/meshtastic/app/navigation/NodesNavigation.kt index d9fded5b4..8d628a96c 100644 --- a/app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt +++ b/app/src/main/kotlin/org/meshtastic/app/navigation/NodesNavigation.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/navigation/SettingsNavigation.kt b/app/src/main/kotlin/org/meshtastic/app/navigation/SettingsNavigation.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/navigation/SettingsNavigation.kt rename to app/src/main/kotlin/org/meshtastic/app/navigation/SettingsNavigation.kt index eacec7cb3..eebe1db28 100644 --- a/app/src/main/java/com/geeksville/mesh/navigation/SettingsNavigation.kt +++ b/app/src/main/kotlin/org/meshtastic/app/navigation/SettingsNavigation.kt @@ -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 diff --git a/app/src/main/java/com/geeksville/mesh/repository/network/ConnectivityManager.kt b/app/src/main/kotlin/org/meshtastic/app/repository/network/ConnectivityManager.kt similarity index 63% rename from app/src/main/java/com/geeksville/mesh/repository/network/ConnectivityManager.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/network/ConnectivityManager.kt index b7944344e..14e205845 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/network/ConnectivityManager.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/network/ConnectivityManager.kt @@ -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 . */ - -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 = observeNetworks() - .map { activeNetworksList -> activeNetworksList.isNotEmpty() } - .distinctUntilChanged() +internal fun ConnectivityManager.networkAvailable(): Flow = + 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() - 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) } } diff --git a/app/src/main/java/com/geeksville/mesh/repository/network/NetworkRepository.kt b/app/src/main/kotlin/org/meshtastic/app/repository/network/NetworkRepository.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/repository/network/NetworkRepository.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/network/NetworkRepository.kt index 2266cdc4f..eeda06b17 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/network/NetworkRepository.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/network/NetworkRepository.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.network +package org.meshtastic.app.repository.network import android.net.ConnectivityManager import android.net.nsd.NsdManager diff --git a/app/src/main/java/com/geeksville/mesh/repository/network/NetworkRepositoryModule.kt b/app/src/main/kotlin/org/meshtastic/app/repository/network/NetworkRepositoryModule.kt similarity index 78% rename from app/src/main/java/com/geeksville/mesh/repository/network/NetworkRepositoryModule.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/network/NetworkRepositoryModule.kt index 21812f5e8..573ae4d9b 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/network/NetworkRepositoryModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/network/NetworkRepositoryModule.kt @@ -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 . */ - -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 } } diff --git a/app/src/main/java/com/geeksville/mesh/repository/network/NsdManager.kt b/app/src/main/kotlin/org/meshtastic/app/repository/network/NsdManager.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/repository/network/NsdManager.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/network/NsdManager.kt index a255dea72..167da39a6 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/network/NsdManager.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/network/NsdManager.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.network +package org.meshtastic.app.repository.network import android.annotation.SuppressLint import android.net.nsd.NsdManager diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/AndroidRadioInterfaceService.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/AndroidRadioInterfaceService.kt similarity index 89% rename from app/src/main/java/com/geeksville/mesh/repository/radio/AndroidRadioInterfaceService.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/AndroidRadioInterfaceService.kt index bab2fc843..4c2547a75 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/AndroidRadioInterfaceService.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/AndroidRadioInterfaceService.kt @@ -14,15 +14,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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(extraBufferCapacity = 64) + private val _meshActivity = + MutableSharedFlow( + extraBufferCapacity = 64, + onBufferOverflow = kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST, + ) override val meshActivity: SharedFlow = _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) } } diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/IRadioInterface.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/IRadioInterface.kt similarity index 92% rename from app/src/main/java/com/geeksville/mesh/repository/radio/IRadioInterface.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/IRadioInterface.kt index 7690bebea..ddf7f0da7 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/IRadioInterface.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/IRadioInterface.kt @@ -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 . */ - -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import java.io.Closeable diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceFactory.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceFactory.kt similarity index 97% rename from app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceFactory.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceFactory.kt index f511cb555..dc6c1204d 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceFactory.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceFactory.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import org.meshtastic.core.model.InterfaceId import javax.inject.Inject diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceFactorySpi.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceFactorySpi.kt similarity index 65% rename from app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceFactorySpi.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceFactorySpi.kt index ed35a524c..8d78affd1 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceFactorySpi.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceFactorySpi.kt @@ -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 . */ - -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 { fun create(rest: String): T diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceMapKey.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceMapKey.kt similarity index 95% rename from app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceMapKey.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceMapKey.kt index fc9170c6a..4864abe7a 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceMapKey.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceMapKey.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import dagger.MapKey import org.meshtastic.core.model.InterfaceId diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceSpec.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceSpec.kt similarity index 82% rename from app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceSpec.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceSpec.kt index a437f9932..5bfede5cd 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/InterfaceSpec.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceSpec.kt @@ -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 . */ +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 { 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 -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/MeshtasticRadioProfile.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/MeshtasticRadioProfile.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/repository/radio/MeshtasticRadioProfile.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/MeshtasticRadioProfile.kt index 512b04fdd..bdab7ad72 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/MeshtasticRadioProfile.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/MeshtasticRadioProfile.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import kotlinx.coroutines.flow.Flow diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/MeshtasticRadioServiceImpl.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/MeshtasticRadioServiceImpl.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/repository/radio/MeshtasticRadioServiceImpl.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/MeshtasticRadioServiceImpl.kt index 266df6651..30380546a 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/MeshtasticRadioServiceImpl.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/MeshtasticRadioServiceImpl.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/MockInterface.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/MockInterface.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/repository/radio/MockInterface.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/MockInterface.kt index 2dc509ed2..4059b4e33 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/MockInterface.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/MockInterface.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import co.touchlab.kermit.Logger import dagger.assisted.Assisted diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/MockInterfaceFactory.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/MockInterfaceFactory.kt similarity index 84% rename from app/src/main/java/com/geeksville/mesh/repository/radio/MockInterfaceFactory.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/MockInterfaceFactory.kt index 689b16a42..f25aa828f 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/MockInterfaceFactory.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/MockInterfaceFactory.kt @@ -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 . */ - -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 -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/MockInterfaceSpec.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/MockInterfaceSpec.kt similarity index 69% rename from app/src/main/java/com/geeksville/mesh/repository/radio/MockInterfaceSpec.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/MockInterfaceSpec.kt index f2bdb7183..4a6a1862f 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/MockInterfaceSpec.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/MockInterfaceSpec.kt @@ -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 . */ - -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 { - override fun createInterface(rest: String): MockInterface { - return factory.create(rest) - } +/** Mock interface backend implementation. */ +class MockInterfaceSpec @Inject constructor(private val factory: MockInterfaceFactory) : InterfaceSpec { + 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 diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/NopInterface.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NopInterface.kt similarity index 88% rename from app/src/main/java/com/geeksville/mesh/repository/radio/NopInterface.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/NopInterface.kt index 369e2ec89..60f30c743 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/NopInterface.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NopInterface.kt @@ -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 . */ - -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 } - -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/NopInterfaceFactory.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NopInterfaceFactory.kt similarity index 84% rename from app/src/main/java/com/geeksville/mesh/repository/radio/NopInterfaceFactory.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/NopInterfaceFactory.kt index 9ba0f32de..e7b29e93d 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/NopInterfaceFactory.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NopInterfaceFactory.kt @@ -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 . */ - -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 -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/NopInterfaceSpec.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NopInterfaceSpec.kt similarity index 65% rename from app/src/main/java/com/geeksville/mesh/repository/radio/NopInterfaceSpec.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/NopInterfaceSpec.kt index 21bd9a319..791209c1b 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/NopInterfaceSpec.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NopInterfaceSpec.kt @@ -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 . */ - -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 { - override fun createInterface(rest: String): NopInterface { - return factory.create(rest) - } +/** No-op interface backend implementation. */ +class NopInterfaceSpec @Inject constructor(private val factory: NopInterfaceFactory) : InterfaceSpec { + override fun createInterface(rest: String): NopInterface = factory.create(rest) } diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterface.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NordicBleInterface.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterface.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/NordicBleInterface.kt index 7e06206ba..155eaec8a 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterface.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NordicBleInterface.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import android.annotation.SuppressLint import co.touchlab.kermit.Logger diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceFactory.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceFactory.kt similarity index 90% rename from app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceFactory.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceFactory.kt index 42ae6e3e7..76835ffaf 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceFactory.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceFactory.kt @@ -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 . */ - -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import dagger.assisted.AssistedFactory diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceSpec.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceSpec.kt similarity index 97% rename from app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceSpec.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceSpec.kt index 112d38e29..d7b03d1a2 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceSpec.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceSpec.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import co.touchlab.kermit.Logger import org.meshtastic.core.ble.BluetoothRepository diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioRepositoryModule.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/RadioRepositoryModule.kt similarity index 97% rename from app/src/main/java/com/geeksville/mesh/repository/radio/RadioRepositoryModule.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/RadioRepositoryModule.kt index 88d957917..01a715312 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioRepositoryModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/RadioRepositoryModule.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import dagger.Binds import dagger.Module diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterface.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/SerialInterface.kt similarity index 95% rename from app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterface.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/SerialInterface.kt index 04d67b879..39992f67b 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterface.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/SerialInterface.kt @@ -14,14 +14,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterfaceFactory.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/SerialInterfaceFactory.kt similarity index 84% rename from app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterfaceFactory.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/SerialInterfaceFactory.kt index 88d8a8527..ef518d324 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterfaceFactory.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/SerialInterfaceFactory.kt @@ -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 . */ - -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 -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterfaceSpec.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/SerialInterfaceSpec.kt similarity index 94% rename from app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterfaceSpec.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/SerialInterfaceSpec.kt index 294e5eb1d..874210352 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterfaceSpec.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/SerialInterfaceSpec.kt @@ -14,11 +14,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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. */ diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/StreamInterface.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/StreamInterface.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/repository/radio/StreamInterface.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/StreamInterface.kt index 973c38838..0d35e6b8e 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/StreamInterface.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/StreamInterface.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import co.touchlab.kermit.Logger import kotlinx.coroutines.launch diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterface.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/TCPInterface.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterface.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/TCPInterface.kt index a6a8320a5..4ba551f2e 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterface.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/TCPInterface.kt @@ -14,14 +14,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterfaceFactory.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/TCPInterfaceFactory.kt similarity index 84% rename from app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterfaceFactory.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/TCPInterfaceFactory.kt index abeee0556..1a96d9537 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterfaceFactory.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/TCPInterfaceFactory.kt @@ -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 . */ - -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 -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterfaceSpec.kt b/app/src/main/kotlin/org/meshtastic/app/repository/radio/TCPInterfaceSpec.kt similarity index 65% rename from app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterfaceSpec.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/radio/TCPInterfaceSpec.kt index 0af23257a..b5a9e1ed1 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterfaceSpec.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/radio/TCPInterfaceSpec.kt @@ -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 . */ - -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 { - override fun createInterface(rest: String): TCPInterface { - return factory.create(rest) - } +/** TCP interface backend implementation. */ +class TCPInterfaceSpec @Inject constructor(private val factory: TCPInterfaceFactory) : InterfaceSpec { + override fun createInterface(rest: String): TCPInterface = factory.create(rest) } diff --git a/app/src/main/java/com/geeksville/mesh/repository/usb/ProbeTableProvider.kt b/app/src/main/kotlin/org/meshtastic/app/repository/usb/ProbeTableProvider.kt similarity index 63% rename from app/src/main/java/com/geeksville/mesh/repository/usb/ProbeTableProvider.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/usb/ProbeTableProvider.kt index 8354ca6dd..9d8a21bae 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/usb/ProbeTableProvider.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/usb/ProbeTableProvider.kt @@ -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 . */ - -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 { - 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) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/usb/README.md b/app/src/main/kotlin/org/meshtastic/app/repository/usb/README.md similarity index 100% rename from app/src/main/java/com/geeksville/mesh/repository/usb/README.md rename to app/src/main/kotlin/org/meshtastic/app/repository/usb/README.md diff --git a/app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnection.kt b/app/src/main/kotlin/org/meshtastic/app/repository/usb/SerialConnection.kt similarity index 74% rename from app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnection.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/usb/SerialConnection.kt index 89d712618..fa5d5bf6f 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnection.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/usb/SerialConnection.kt @@ -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 . */ +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() -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnectionImpl.kt b/app/src/main/kotlin/org/meshtastic/app/repository/usb/SerialConnectionImpl.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnectionImpl.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/usb/SerialConnectionImpl.kt index 44aed0ba2..bfd959ef2 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnectionImpl.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/usb/SerialConnectionImpl.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.usb +package org.meshtastic.app.repository.usb import android.hardware.usb.UsbManager import co.touchlab.kermit.Logger diff --git a/app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnectionListener.kt b/app/src/main/kotlin/org/meshtastic/app/repository/usb/SerialConnectionListener.kt similarity index 63% rename from app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnectionListener.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/usb/SerialConnectionListener.kt index 72238ea96..ef2684d20 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnectionListener.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/usb/SerialConnectionListener.kt @@ -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 . */ +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?) {} -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/repository/usb/UsbBroadcastReceiver.kt b/app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbBroadcastReceiver.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/repository/usb/UsbBroadcastReceiver.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbBroadcastReceiver.kt index 255abb308..6be9c82c4 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/usb/UsbBroadcastReceiver.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbBroadcastReceiver.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.usb +package org.meshtastic.app.repository.usb import android.content.BroadcastReceiver import android.content.Context diff --git a/app/src/main/java/com/geeksville/mesh/repository/usb/UsbManager.kt b/app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbManager.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/repository/usb/UsbManager.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbManager.kt index 9bdac49e2..c0e6e4a05 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/usb/UsbManager.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbManager.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.usb +package org.meshtastic.app.repository.usb import android.content.BroadcastReceiver import android.content.Context diff --git a/app/src/main/java/com/geeksville/mesh/repository/usb/UsbRepository.kt b/app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbRepository.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/repository/usb/UsbRepository.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbRepository.kt index 71ba5d04b..3f9aad9ba 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/usb/UsbRepository.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbRepository.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.usb +package org.meshtastic.app.repository.usb import android.app.Application import android.hardware.usb.UsbDevice diff --git a/app/src/main/java/com/geeksville/mesh/repository/usb/UsbRepositoryModule.kt b/app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbRepositoryModule.kt similarity index 80% rename from app/src/main/java/com/geeksville/mesh/repository/usb/UsbRepositoryModule.kt rename to app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbRepositoryModule.kt index 8aeb0abd7..7396619fa 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/usb/UsbRepositoryModule.kt +++ b/app/src/main/kotlin/org/meshtastic/app/repository/usb/UsbRepositoryModule.kt @@ -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 . */ - -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) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/geeksville/mesh/service/AndroidAppWidgetUpdater.kt b/app/src/main/kotlin/org/meshtastic/app/service/AndroidAppWidgetUpdater.kt similarity index 94% rename from app/src/main/java/com/geeksville/mesh/service/AndroidAppWidgetUpdater.kt rename to app/src/main/kotlin/org/meshtastic/app/service/AndroidAppWidgetUpdater.kt index 9735b0ab5..f43935611 100644 --- a/app/src/main/java/com/geeksville/mesh/service/AndroidAppWidgetUpdater.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/AndroidAppWidgetUpdater.kt @@ -14,12 +14,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/service/AndroidMeshLocationManager.kt b/app/src/main/kotlin/org/meshtastic/app/service/AndroidMeshLocationManager.kt similarity index 97% rename from app/src/main/java/com/geeksville/mesh/service/AndroidMeshLocationManager.kt rename to app/src/main/kotlin/org/meshtastic/app/service/AndroidMeshLocationManager.kt index 7ab35c151..c3d9d58f3 100644 --- a/app/src/main/java/com/geeksville/mesh/service/AndroidMeshLocationManager.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/AndroidMeshLocationManager.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/service/AndroidMeshWorkerManager.kt b/app/src/main/kotlin/org/meshtastic/app/service/AndroidMeshWorkerManager.kt similarity index 97% rename from app/src/main/java/com/geeksville/mesh/service/AndroidMeshWorkerManager.kt rename to app/src/main/kotlin/org/meshtastic/app/service/AndroidMeshWorkerManager.kt index 8b235ea5c..56038c94e 100644 --- a/app/src/main/java/com/geeksville/mesh/service/AndroidMeshWorkerManager.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/AndroidMeshWorkerManager.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.service +package org.meshtastic.app.service import androidx.work.ExistingWorkPolicy import androidx.work.OneTimeWorkRequestBuilder diff --git a/app/src/main/java/com/geeksville/mesh/service/BootCompleteReceiver.kt b/app/src/main/kotlin/org/meshtastic/app/service/BootCompleteReceiver.kt similarity index 94% rename from app/src/main/java/com/geeksville/mesh/service/BootCompleteReceiver.kt rename to app/src/main/kotlin/org/meshtastic/app/service/BootCompleteReceiver.kt index c7e5d0773..732be7b19 100644 --- a/app/src/main/java/com/geeksville/mesh/service/BootCompleteReceiver.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/BootCompleteReceiver.kt @@ -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 . */ - -package com.geeksville.mesh.service +package org.meshtastic.app.service import android.content.BroadcastReceiver import android.content.Context diff --git a/app/src/main/java/com/geeksville/mesh/service/Constants.kt b/app/src/main/kotlin/org/meshtastic/app/service/Constants.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/service/Constants.kt rename to app/src/main/kotlin/org/meshtastic/app/service/Constants.kt index f350d6c28..af5fdbdcd 100644 --- a/app/src/main/java/com/geeksville/mesh/service/Constants.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/Constants.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.service +package org.meshtastic.app.service import org.meshtastic.core.api.MeshtasticIntent diff --git a/app/src/main/java/com/geeksville/mesh/service/MarkAsReadReceiver.kt b/app/src/main/kotlin/org/meshtastic/app/service/MarkAsReadReceiver.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/service/MarkAsReadReceiver.kt rename to app/src/main/kotlin/org/meshtastic/app/service/MarkAsReadReceiver.kt index 23f6b1737..76b66bdbf 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MarkAsReadReceiver.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/MarkAsReadReceiver.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.service +package org.meshtastic.app.service import android.content.BroadcastReceiver import android.content.Context diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/kotlin/org/meshtastic/app/service/MeshService.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/service/MeshService.kt rename to app/src/main/kotlin/org/meshtastic/app/service/MeshService.kt index cf97cd5c2..83e2a996f 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/MeshService.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshServiceNotificationsImpl.kt b/app/src/main/kotlin/org/meshtastic/app/service/MeshServiceNotificationsImpl.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/service/MeshServiceNotificationsImpl.kt rename to app/src/main/kotlin/org/meshtastic/app/service/MeshServiceNotificationsImpl.kt index 47b0a7fb2..a7680c117 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshServiceNotificationsImpl.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/MeshServiceNotificationsImpl.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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) diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshServiceStarter.kt b/app/src/main/kotlin/org/meshtastic/app/service/MeshServiceStarter.kt similarity index 94% rename from app/src/main/java/com/geeksville/mesh/service/MeshServiceStarter.kt rename to app/src/main/kotlin/org/meshtastic/app/service/MeshServiceStarter.kt index da1e006d7..96ea0d9bf 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshServiceStarter.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/MeshServiceStarter.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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) { diff --git a/app/src/main/java/com/geeksville/mesh/service/ReactionReceiver.kt b/app/src/main/kotlin/org/meshtastic/app/service/ReactionReceiver.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/service/ReactionReceiver.kt rename to app/src/main/kotlin/org/meshtastic/app/service/ReactionReceiver.kt index bea76c147..cd3f32c5b 100644 --- a/app/src/main/java/com/geeksville/mesh/service/ReactionReceiver.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/ReactionReceiver.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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" diff --git a/app/src/main/java/com/geeksville/mesh/service/ReplyReceiver.kt b/app/src/main/kotlin/org/meshtastic/app/service/ReplyReceiver.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/service/ReplyReceiver.kt rename to app/src/main/kotlin/org/meshtastic/app/service/ReplyReceiver.kt index e21039670..190915b3f 100644 --- a/app/src/main/java/com/geeksville/mesh/service/ReplyReceiver.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/ReplyReceiver.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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" } diff --git a/app/src/main/java/com/geeksville/mesh/service/ServiceBroadcasts.kt b/app/src/main/kotlin/org/meshtastic/app/service/ServiceBroadcasts.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/service/ServiceBroadcasts.kt rename to app/src/main/kotlin/org/meshtastic/app/service/ServiceBroadcasts.kt index 99d0bc724..86845e25b 100644 --- a/app/src/main/java/com/geeksville/mesh/service/ServiceBroadcasts.kt +++ b/app/src/main/kotlin/org/meshtastic/app/service/ServiceBroadcasts.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.service +package org.meshtastic.app.service import android.content.Context import android.content.Intent diff --git a/app/src/main/java/com/geeksville/mesh/ui/Main.kt b/app/src/main/kotlin/org/meshtastic/app/ui/Main.kt similarity index 97% rename from app/src/main/java/com/geeksville/mesh/ui/Main.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/Main.kt index 153b4dbac..adcab19c5 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Main.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/Main.kt @@ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/ConnectionsScreen.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/ConnectionsScreen.kt index 27cb87e24..5f4e34e29 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/ConnectionsScreen.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsViewModel.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/ConnectionsViewModel.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsViewModel.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/ConnectionsViewModel.kt index e7a363725..8205ff0c0 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsViewModel.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/ConnectionsViewModel.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.ui.connections +package org.meshtastic.app.ui.connections import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/DeviceType.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/DeviceType.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/ui/connections/DeviceType.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/DeviceType.kt index 102f209e5..5048acf30 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/DeviceType.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/DeviceType.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 { diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/ScannerViewModel.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/ScannerViewModel.kt similarity index 97% rename from app/src/main/java/com/geeksville/mesh/ui/connections/ScannerViewModel.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/ScannerViewModel.kt index 0bfba1faf..0ea247a12 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/ScannerViewModel.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/ScannerViewModel.kt @@ -14,15 +14,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/BLEDevices.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/BLEDevices.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/ui/connections/components/BLEDevices.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/components/BLEDevices.kt index 2ea0bda92..960fcda6b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/BLEDevices.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/BLEDevices.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectingDeviceInfo.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/ConnectingDeviceInfo.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectingDeviceInfo.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/components/ConnectingDeviceInfo.kt index 86341f2ce..4b0b7348a 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectingDeviceInfo.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/ConnectingDeviceInfo.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectionsNavIcon.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/ConnectionsNavIcon.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectionsNavIcon.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/components/ConnectionsNavIcon.kt index 03be8458b..2efb59df1 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectionsNavIcon.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/ConnectionsNavIcon.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectionsSegmentedBar.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/ConnectionsSegmentedBar.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectionsSegmentedBar.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/components/ConnectionsSegmentedBar.kt index 705918d9a..56944177c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectionsSegmentedBar.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/ConnectionsSegmentedBar.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/CurrentlyConnectedInfo.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/components/CurrentlyConnectedInfo.kt index 9bf5f3fbc..16f7af6ec 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/CurrentlyConnectedInfo.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/DeviceListItem.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/DeviceListItem.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/ui/connections/components/DeviceListItem.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/components/DeviceListItem.kt index 0ab39bbe7..8fe790763 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/DeviceListItem.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/DeviceListItem.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/DeviceListSection.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/DeviceListSection.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/ui/connections/components/DeviceListSection.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/components/DeviceListSection.kt index 2381d4f97..020ff91a3 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/DeviceListSection.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/DeviceListSection.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/EmptyStateContent.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/EmptyStateContent.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/ui/connections/components/EmptyStateContent.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/components/EmptyStateContent.kt index 2a0b572e2..28d0131c3 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/EmptyStateContent.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/EmptyStateContent.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/NetworkDevices.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/NetworkDevices.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/ui/connections/components/NetworkDevices.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/components/NetworkDevices.kt index 8cda4687c..c6c92500c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/NetworkDevices.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/NetworkDevices.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/UsbDevices.kt b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/UsbDevices.kt similarity index 92% rename from app/src/main/java/com/geeksville/mesh/ui/connections/components/UsbDevices.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/connections/components/UsbDevices.kt index 9669e83c8..07fa2d50b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/UsbDevices.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/connections/components/UsbDevices.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 diff --git a/app/src/main/java/com/geeksville/mesh/ui/node/AdaptiveNodeListScreen.kt b/app/src/main/kotlin/org/meshtastic/app/ui/node/AdaptiveNodeListScreen.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/ui/node/AdaptiveNodeListScreen.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/node/AdaptiveNodeListScreen.kt index fdef5b4bd..f50acc4e7 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/node/AdaptiveNodeListScreen.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/node/AdaptiveNodeListScreen.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.ui.node +package org.meshtastic.app.ui.node import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Arrangement diff --git a/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt b/app/src/main/kotlin/org/meshtastic/app/ui/sharing/Channel.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/sharing/Channel.kt index 24bcff02f..627822b9a 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/sharing/Channel.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.ui.sharing +package org.meshtastic.app.ui.sharing import android.net.Uri import android.os.RemoteException diff --git a/app/src/main/java/com/geeksville/mesh/ui/sharing/ChannelViewModel.kt b/app/src/main/kotlin/org/meshtastic/app/ui/sharing/ChannelViewModel.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/ui/sharing/ChannelViewModel.kt rename to app/src/main/kotlin/org/meshtastic/app/ui/sharing/ChannelViewModel.kt index c5ba9bec4..0fad35a09 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/sharing/ChannelViewModel.kt +++ b/app/src/main/kotlin/org/meshtastic/app/ui/sharing/ChannelViewModel.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.ui.sharing +package org.meshtastic.app.ui.sharing import android.net.Uri import androidx.lifecycle.ViewModel @@ -24,10 +24,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import org.meshtastic.core.analytics.DataPair -import org.meshtastic.core.analytics.platform.PlatformAnalytics import org.meshtastic.core.model.RadioController import org.meshtastic.core.model.util.toChannelSet +import org.meshtastic.core.repository.DataPair +import org.meshtastic.core.repository.PlatformAnalytics import org.meshtastic.core.repository.RadioConfigRepository import org.meshtastic.core.ui.util.getChannelList import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed diff --git a/app/src/main/java/com/geeksville/mesh/widget/LocalStatsWidget.kt b/app/src/main/kotlin/org/meshtastic/app/widget/LocalStatsWidget.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/widget/LocalStatsWidget.kt rename to app/src/main/kotlin/org/meshtastic/app/widget/LocalStatsWidget.kt index 1e7f58323..5753f8040 100644 --- a/app/src/main/java/com/geeksville/mesh/widget/LocalStatsWidget.kt +++ b/app/src/main/kotlin/org/meshtastic/app/widget/LocalStatsWidget.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.widget +package org.meshtastic.app.widget import android.annotation.SuppressLint import android.content.Context @@ -143,11 +143,11 @@ class LocalStatsWidget : GlanceAppWidget() { Scaffold( titleBar = { TitleBar( - startIcon = ImageProvider(com.geeksville.mesh.R.drawable.app_icon), + startIcon = ImageProvider(org.meshtastic.app.R.drawable.app_icon), title = stringResource(Res.string.meshtastic_app_name), actions = { CircleIconButton( - imageProvider = ImageProvider(com.geeksville.mesh.R.drawable.ic_refresh), + imageProvider = ImageProvider(org.meshtastic.app.R.drawable.ic_refresh), contentDescription = stringResource(Res.string.refresh), onClick = actionRunCallback(), backgroundColor = null, @@ -156,7 +156,7 @@ class LocalStatsWidget : GlanceAppWidget() { ) }, modifier = - GlanceModifier.fillMaxSize().clickable(actionStartActivity()), + GlanceModifier.fillMaxSize().clickable(actionStartActivity()), ) { if (state.showContent) { FullStatsContent(state) @@ -300,7 +300,7 @@ class LocalStatsWidget : GlanceAppWidget() { CircularProgressIndicator(modifier = GlanceModifier.size(24.dp)) } else { Image( - provider = ImageProvider(com.geeksville.mesh.R.drawable.app_icon), + provider = ImageProvider(org.meshtastic.app.R.drawable.app_icon), contentDescription = null, modifier = GlanceModifier.size(32.dp), ) diff --git a/app/src/main/java/com/geeksville/mesh/widget/LocalStatsWidgetReceiver.kt b/app/src/main/kotlin/org/meshtastic/app/widget/LocalStatsWidgetReceiver.kt similarity index 96% rename from app/src/main/java/com/geeksville/mesh/widget/LocalStatsWidgetReceiver.kt rename to app/src/main/kotlin/org/meshtastic/app/widget/LocalStatsWidgetReceiver.kt index 39719efb4..2b162b9b8 100644 --- a/app/src/main/java/com/geeksville/mesh/widget/LocalStatsWidgetReceiver.kt +++ b/app/src/main/kotlin/org/meshtastic/app/widget/LocalStatsWidgetReceiver.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.widget +package org.meshtastic.app.widget import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.GlanceAppWidgetReceiver diff --git a/app/src/main/java/com/geeksville/mesh/widget/LocalStatsWidgetState.kt b/app/src/main/kotlin/org/meshtastic/app/widget/LocalStatsWidgetState.kt similarity index 99% rename from app/src/main/java/com/geeksville/mesh/widget/LocalStatsWidgetState.kt rename to app/src/main/kotlin/org/meshtastic/app/widget/LocalStatsWidgetState.kt index d11868e7a..b4d643d43 100644 --- a/app/src/main/java/com/geeksville/mesh/widget/LocalStatsWidgetState.kt +++ b/app/src/main/kotlin/org/meshtastic/app/widget/LocalStatsWidgetState.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.widget +package org.meshtastic.app.widget import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/java/com/geeksville/mesh/widget/RefreshLocalStatsAction.kt b/app/src/main/kotlin/org/meshtastic/app/widget/RefreshLocalStatsAction.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/widget/RefreshLocalStatsAction.kt rename to app/src/main/kotlin/org/meshtastic/app/widget/RefreshLocalStatsAction.kt index 6a044c90e..e8a060681 100644 --- a/app/src/main/java/com/geeksville/mesh/widget/RefreshLocalStatsAction.kt +++ b/app/src/main/kotlin/org/meshtastic/app/widget/RefreshLocalStatsAction.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.widget +package org.meshtastic.app.widget import android.content.Context import androidx.glance.GlanceId diff --git a/app/src/main/java/com/geeksville/mesh/worker/MeshLogCleanupWorker.kt b/app/src/main/kotlin/org/meshtastic/app/worker/MeshLogCleanupWorker.kt similarity index 98% rename from app/src/main/java/com/geeksville/mesh/worker/MeshLogCleanupWorker.kt rename to app/src/main/kotlin/org/meshtastic/app/worker/MeshLogCleanupWorker.kt index d84e961bd..e4e34a99d 100644 --- a/app/src/main/java/com/geeksville/mesh/worker/MeshLogCleanupWorker.kt +++ b/app/src/main/kotlin/org/meshtastic/app/worker/MeshLogCleanupWorker.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.worker +package org.meshtastic.app.worker import android.content.Context import androidx.hilt.work.HiltWorker diff --git a/app/src/main/java/com/geeksville/mesh/worker/ServiceKeepAliveWorker.kt b/app/src/main/kotlin/org/meshtastic/app/worker/ServiceKeepAliveWorker.kt similarity index 95% rename from app/src/main/java/com/geeksville/mesh/worker/ServiceKeepAliveWorker.kt rename to app/src/main/kotlin/org/meshtastic/app/worker/ServiceKeepAliveWorker.kt index a468896fb..ec443d408 100644 --- a/app/src/main/java/com/geeksville/mesh/worker/ServiceKeepAliveWorker.kt +++ b/app/src/main/kotlin/org/meshtastic/app/worker/ServiceKeepAliveWorker.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.worker +package org.meshtastic.app.worker import android.app.Notification import android.content.Context @@ -26,11 +26,11 @@ import androidx.work.CoroutineWorker import androidx.work.ForegroundInfo import androidx.work.WorkerParameters import co.touchlab.kermit.Logger -import com.geeksville.mesh.R -import com.geeksville.mesh.service.MeshService -import com.geeksville.mesh.service.startService import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import org.meshtastic.app.R +import org.meshtastic.app.service.MeshService +import org.meshtastic.app.service.startService import org.meshtastic.core.repository.MeshServiceNotifications import org.meshtastic.core.repository.SERVICE_NOTIFY_ID diff --git a/app/src/test/java/com/geeksville/mesh/MeshTestApplication.kt b/app/src/test/kotlin/org/meshtastic/app/MeshTestApplication.kt similarity index 98% rename from app/src/test/java/com/geeksville/mesh/MeshTestApplication.kt rename to app/src/test/kotlin/org/meshtastic/app/MeshTestApplication.kt index 23bb532dd..45381aa98 100644 --- a/app/src/test/java/com/geeksville/mesh/MeshTestApplication.kt +++ b/app/src/test/kotlin/org/meshtastic/app/MeshTestApplication.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh +package org.meshtastic.app import androidx.work.Configuration import dagger.hilt.android.EntryPointAccessors diff --git a/app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceRetryTest.kt b/app/src/test/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceRetryTest.kt similarity index 99% rename from app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceRetryTest.kt rename to app/src/test/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceRetryTest.kt index 244167e5c..c9abcdf5e 100644 --- a/app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceRetryTest.kt +++ b/app/src/test/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceRetryTest.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import co.touchlab.kermit.Logger import co.touchlab.kermit.Severity diff --git a/app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceTest.kt b/app/src/test/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceTest.kt similarity index 99% rename from app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceTest.kt rename to app/src/test/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceTest.kt index 1bf2f5a29..d737e7671 100644 --- a/app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceTest.kt +++ b/app/src/test/kotlin/org/meshtastic/app/repository/radio/NordicBleInterfaceTest.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import co.touchlab.kermit.Logger import co.touchlab.kermit.Severity diff --git a/app/src/test/java/com/geeksville/mesh/repository/radio/StreamInterfaceTest.kt b/app/src/test/kotlin/org/meshtastic/app/repository/radio/StreamInterfaceTest.kt similarity index 98% rename from app/src/test/java/com/geeksville/mesh/repository/radio/StreamInterfaceTest.kt rename to app/src/test/kotlin/org/meshtastic/app/repository/radio/StreamInterfaceTest.kt index 868c5197f..865969340 100644 --- a/app/src/test/java/com/geeksville/mesh/repository/radio/StreamInterfaceTest.kt +++ b/app/src/test/kotlin/org/meshtastic/app/repository/radio/StreamInterfaceTest.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio import io.mockk.confirmVerified import io.mockk.mockk diff --git a/app/src/test/java/com/geeksville/mesh/repository/radio/TCPInterfaceTest.kt b/app/src/test/kotlin/org/meshtastic/app/repository/radio/TCPInterfaceTest.kt similarity index 95% rename from app/src/test/java/com/geeksville/mesh/repository/radio/TCPInterfaceTest.kt rename to app/src/test/kotlin/org/meshtastic/app/repository/radio/TCPInterfaceTest.kt index 187916c74..fa124f054 100644 --- a/app/src/test/java/com/geeksville/mesh/repository/radio/TCPInterfaceTest.kt +++ b/app/src/test/kotlin/org/meshtastic/app/repository/radio/TCPInterfaceTest.kt @@ -14,15 +14,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.repository.radio +package org.meshtastic.app.repository.radio -import com.geeksville.mesh.service.Fakes import io.mockk.every import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.test.UnconfinedTestDispatcher import org.junit.Assert.assertEquals import org.junit.Test +import org.meshtastic.app.service.Fakes import org.meshtastic.core.di.CoroutineDispatchers import org.meshtastic.proto.Heartbeat import org.meshtastic.proto.ToRadio diff --git a/app/src/test/java/com/geeksville/mesh/service/Fakes.kt b/app/src/test/kotlin/org/meshtastic/app/service/Fakes.kt similarity index 98% rename from app/src/test/java/com/geeksville/mesh/service/Fakes.kt rename to app/src/test/kotlin/org/meshtastic/app/service/Fakes.kt index 86ecc7fb9..53a35f113 100644 --- a/app/src/test/java/com/geeksville/mesh/service/Fakes.kt +++ b/app/src/test/kotlin/org/meshtastic/app/service/Fakes.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.service +package org.meshtastic.app.service import android.app.Notification import io.mockk.mockk diff --git a/app/src/test/java/com/geeksville/mesh/service/ServiceBroadcastsTest.kt b/app/src/test/kotlin/org/meshtastic/app/service/ServiceBroadcastsTest.kt similarity index 96% rename from app/src/test/java/com/geeksville/mesh/service/ServiceBroadcastsTest.kt rename to app/src/test/kotlin/org/meshtastic/app/service/ServiceBroadcastsTest.kt index 3ddfecd61..0f90d22d2 100644 --- a/app/src/test/java/com/geeksville/mesh/service/ServiceBroadcastsTest.kt +++ b/app/src/test/kotlin/org/meshtastic/app/service/ServiceBroadcastsTest.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.service +package org.meshtastic.app.service import android.app.Application import android.content.Context @@ -30,10 +30,8 @@ import org.meshtastic.core.model.ConnectionState import org.meshtastic.core.repository.ServiceRepository import org.robolectric.RobolectricTestRunner import org.robolectric.Shadows.shadowOf -import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) -@Config(sdk = [34]) class ServiceBroadcastsTest { private lateinit var context: Context diff --git a/app/src/test/java/com/geeksville/mesh/ui/UIUnitTest.kt b/app/src/test/kotlin/org/meshtastic/app/ui/UIUnitTest.kt similarity index 98% rename from app/src/test/java/com/geeksville/mesh/ui/UIUnitTest.kt rename to app/src/test/kotlin/org/meshtastic/app/ui/UIUnitTest.kt index 18e0f4ead..13b68c5e2 100644 --- a/app/src/test/java/com/geeksville/mesh/ui/UIUnitTest.kt +++ b/app/src/test/kotlin/org/meshtastic/app/ui/UIUnitTest.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.ui +package org.meshtastic.app.ui import org.junit.Assert.assertEquals import org.junit.Test diff --git a/app/src/test/java/com/geeksville/mesh/ui/metrics/EnvironmentMetricsTest.kt b/app/src/test/kotlin/org/meshtastic/app/ui/metrics/EnvironmentMetricsTest.kt similarity index 98% rename from app/src/test/java/com/geeksville/mesh/ui/metrics/EnvironmentMetricsTest.kt rename to app/src/test/kotlin/org/meshtastic/app/ui/metrics/EnvironmentMetricsTest.kt index a83894107..00881207e 100644 --- a/app/src/test/java/com/geeksville/mesh/ui/metrics/EnvironmentMetricsTest.kt +++ b/app/src/test/kotlin/org/meshtastic/app/ui/metrics/EnvironmentMetricsTest.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.geeksville.mesh.ui.metrics +package org.meshtastic.app.ui.metrics import org.junit.Assert.assertEquals import org.junit.Test diff --git a/app/src/test/resources/robolectric.properties b/app/src/test/resources/robolectric.properties index 24613e7b1..979b5eebc 100644 --- a/app/src/test/resources/robolectric.properties +++ b/app/src/test/resources/robolectric.properties @@ -1 +1 @@ -application = com.geeksville.mesh.MeshTestApplication +sdk=34 diff --git a/core/analytics/README.md b/core/analytics/README.md deleted file mode 100644 index 218691c2b..000000000 --- a/core/analytics/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# `:core:analytics` - -## Overview -The `:core:analytics` module provides a unified interface for event tracking and crash reporting. It is designed to strictly separate analytics providers based on the build flavor. - -## Key Components - -### 1. `PlatformAnalytics` -An interface defining the standard operations for tracking events and reporting errors. - -## Flavor Specifics - -- **`google` flavor**: Implements `PlatformAnalytics` using **Firebase Analytics** and **Firebase Crashlytics**. -- **`fdroid` flavor**: Provides a "no-op" implementation that does not collect any user data or report crashes, ensuring FOSS compliance. - -## Module dependency graph - - -```mermaid -graph TB - :core:analytics[analytics]:::android-library - :core:analytics -.-> :core:prefs - -classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; -classDef android-application-compose fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; -classDef android-feature fill:#FFD6A5,stroke:#000,stroke-width:2px,color:#000; -classDef android-library fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000; -classDef android-library-compose fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000; -classDef android-test fill:#A0C4FF,stroke:#000,stroke-width:2px,color:#000; -classDef jvm-library fill:#BDB2FF,stroke:#000,stroke-width:2px,color:#000; -classDef kmp-library fill:#FFC1CC,stroke:#000,stroke-width:2px,color:#000; -classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000; - -``` - diff --git a/core/analytics/build.gradle.kts b/core/analytics/build.gradle.kts deleted file mode 100644 index 5ee46fe82..000000000 --- a/core/analytics/build.gradle.kts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2025-2026 Meshtastic LLC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -import com.android.build.api.dsl.LibraryExtension - -plugins { - alias(libs.plugins.meshtastic.android.library) - alias(libs.plugins.meshtastic.android.library.flavors) - alias(libs.plugins.meshtastic.android.library.compose) - alias(libs.plugins.meshtastic.hilt) - alias(libs.plugins.secrets) - alias(libs.plugins.kover) -} - -dependencies { - implementation(projects.core.prefs) - implementation(projects.core.repository) - - implementation(libs.androidx.compose.runtime) - implementation(libs.androidx.lifecycle.process) - implementation(libs.androidx.navigation.runtime) - implementation(libs.kermit) - - googleApi(libs.dd.sdk.android.compose) - googleApi(libs.dd.sdk.android.logs) - googleApi(libs.dd.sdk.android.rum) - googleApi(libs.dd.sdk.android.timber) - googleApi(libs.dd.sdk.android.trace) - googleApi(libs.dd.sdk.android.trace.otel) - googleApi(platform(libs.firebase.bom)) - googleApi(libs.firebase.analytics) - googleApi(libs.firebase.crashlytics) -} - -configure { - buildFeatures { buildConfig = true } - namespace = "org.meshtastic.core.analytics" -} - -secrets { - defaultPropertiesFileName = "secrets.defaults.properties" - propertiesFileName = "secrets.properties" -} diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index a902d1cc1..09d77c011 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -32,6 +32,7 @@ kotlin { implementation(libs.javax.inject) implementation(libs.kotlinx.coroutines.core) api(libs.kotlinx.datetime) + api(libs.okio) implementation(libs.kermit) } androidMain.dependencies { diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index 6da9b686c..e2bd4480b 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -14,44 +14,59 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import com.android.build.api.dsl.LibraryExtension plugins { - alias(libs.plugins.meshtastic.android.library) - alias(libs.plugins.meshtastic.android.library.flavors) - alias(libs.plugins.meshtastic.hilt) + alias(libs.plugins.meshtastic.kmp.library) alias(libs.plugins.meshtastic.kotlinx.serialization) + alias(libs.plugins.devtools.ksp) } -configure { namespace = "org.meshtastic.core.data" } +kotlin { + @Suppress("UnstableApiUsage") + android { + namespace = "org.meshtastic.core.data" + androidResources.enable = false + withHostTest { isIncludeAndroidResources = true } + } -dependencies { - api(projects.core.repository) - implementation(projects.core.analytics) - implementation(projects.core.common) - implementation(projects.core.database) - implementation(projects.core.datastore) - implementation(libs.androidx.datastore) - implementation(libs.androidx.datastore.preferences) - implementation(projects.core.di) - implementation(projects.core.model) - implementation(projects.core.network) - implementation(projects.core.prefs) - implementation(projects.core.proto) + sourceSets { + commonMain.dependencies { + api(projects.core.repository) + implementation(projects.core.common) + implementation(projects.core.database) + implementation(projects.core.datastore) + implementation(projects.core.di) + implementation(projects.core.model) + implementation(projects.core.network) + implementation(projects.core.prefs) + implementation(projects.core.proto) - // Needed because core:data references MeshtasticDatabase (supertype RoomDatabase) - implementation(libs.androidx.room.runtime) - implementation(libs.androidx.room.paging) - implementation(libs.androidx.sqlite.bundled) + api(libs.javax.inject) + implementation(libs.androidx.lifecycle.runtime) + implementation(libs.androidx.paging.common) + implementation(libs.kotlinx.serialization.json) + implementation(libs.kermit) + implementation(libs.kotlinx.atomicfu) + implementation(libs.kotlinx.collections.immutable) + } - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.lifecycle.runtime) - implementation(libs.androidx.core.location.altitude) - implementation(libs.androidx.paging.common) - implementation(libs.kotlinx.serialization.json) - implementation(libs.kermit) + androidMain.dependencies { + implementation(libs.hilt.android) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.core.location.altitude) - testImplementation(libs.junit) - testImplementation(libs.mockk) - testImplementation(libs.kotlinx.coroutines.test) + // Needed because core:data references MeshtasticDatabase (supertype RoomDatabase) + implementation(libs.androidx.room.runtime) + implementation(libs.androidx.room.paging) + implementation(libs.androidx.sqlite.bundled) + } + + commonTest.dependencies { + implementation(kotlin("test")) + implementation(libs.kotlinx.coroutines.test) + implementation(libs.mockk) + } + } } + +dependencies { add("kspAndroid", libs.hilt.compiler) } diff --git a/core/data/detekt-baseline.xml b/core/data/detekt-baseline.xml index d72692f01..2354a0f89 100644 --- a/core/data/detekt-baseline.xml +++ b/core/data/detekt-baseline.xml @@ -2,10 +2,6 @@ - MagicNumber:LocationRepository.kt$LocationRepository$1000L - MagicNumber:LocationRepository.kt$LocationRepository$30 - MagicNumber:LocationRepository.kt$LocationRepository$31 - TooGenericExceptionCaught:LocationRepository.kt$LocationRepository$e: Exception - TooManyFunctions:PacketRepository.kt$PacketRepository + MaxLineLength:BootloaderOtaQuirksJsonDataSourceImpl.kt$BootloaderOtaQuirksJsonDataSourceImpl$class diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt b/core/data/src/androidMain/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSourceImpl.kt similarity index 83% rename from core/data/src/main/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt rename to core/data/src/androidMain/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSourceImpl.kt index 29376cd16..aa301ed7c 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt +++ b/core/data/src/androidMain/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSourceImpl.kt @@ -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,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.data.datasource import android.app.Application @@ -26,9 +25,10 @@ import kotlinx.serialization.json.decodeFromStream import org.meshtastic.core.model.BootloaderOtaQuirk import javax.inject.Inject -class BootloaderOtaQuirksJsonDataSource @Inject constructor(private val application: Application) { +class BootloaderOtaQuirksJsonDataSourceImpl @Inject constructor(private val application: Application) : + BootloaderOtaQuirksJsonDataSource { @OptIn(ExperimentalSerializationApi::class) - fun loadBootloaderOtaQuirksFromJsonAsset(): List = runCatching { + override fun loadBootloaderOtaQuirksFromJsonAsset(): List = runCatching { val inputStream = application.assets.open("device_bootloader_ota_quirks.json") inputStream.use { Json.decodeFromStream(it).devices } } diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt b/core/data/src/androidMain/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSourceImpl.kt similarity index 85% rename from core/data/src/main/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt rename to core/data/src/androidMain/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSourceImpl.kt index e6caa4003..e741ad476 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt +++ b/core/data/src/androidMain/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSourceImpl.kt @@ -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,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.data.datasource import android.app.Application @@ -24,7 +23,8 @@ import kotlinx.serialization.json.decodeFromStream import org.meshtastic.core.model.NetworkDeviceHardware import javax.inject.Inject -class DeviceHardwareJsonDataSource @Inject constructor(private val application: Application) { +class DeviceHardwareJsonDataSourceImpl @Inject constructor(private val application: Application) : + DeviceHardwareJsonDataSource { // Use a tolerant JSON parser so that additional fields in the bundled asset // (e.g., "key") do not break deserialization on older app versions. @@ -35,7 +35,7 @@ class DeviceHardwareJsonDataSource @Inject constructor(private val application: } @OptIn(ExperimentalSerializationApi::class) - fun loadDeviceHardwareFromJsonAsset(): List = + override fun loadDeviceHardwareFromJsonAsset(): List = application.assets.open("device_hardware.json").use { inputStream -> json.decodeFromStream>(inputStream) } diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt b/core/data/src/androidMain/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSourceImpl.kt similarity index 85% rename from core/data/src/main/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt rename to core/data/src/androidMain/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSourceImpl.kt index a643f2f2b..bc745898c 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt +++ b/core/data/src/androidMain/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSourceImpl.kt @@ -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,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.data.datasource import android.app.Application @@ -24,7 +23,8 @@ import kotlinx.serialization.json.decodeFromStream import org.meshtastic.core.model.NetworkFirmwareReleases import javax.inject.Inject -class FirmwareReleaseJsonDataSource @Inject constructor(private val application: Application) { +class FirmwareReleaseJsonDataSourceImpl @Inject constructor(private val application: Application) : + FirmwareReleaseJsonDataSource { // Match the network client behavior: be tolerant of unknown fields so that // older app versions can read newer snapshots of firmware_releases.json. @@ -35,7 +35,7 @@ class FirmwareReleaseJsonDataSource @Inject constructor(private val application: } @OptIn(ExperimentalSerializationApi::class) - fun loadFirmwareReleaseFromJsonAsset(): NetworkFirmwareReleases = + override fun loadFirmwareReleaseFromJsonAsset(): NetworkFirmwareReleases = application.assets.open("firmware_releases.json").use { inputStream -> json.decodeFromStream(inputStream) } diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/LocationRepository.kt b/core/data/src/androidMain/kotlin/org/meshtastic/core/data/repository/LocationRepositoryImpl.kt similarity index 77% rename from core/data/src/main/kotlin/org/meshtastic/core/data/repository/LocationRepository.kt rename to core/data/src/androidMain/kotlin/org/meshtastic/core/data/repository/LocationRepositoryImpl.kt index a1b7b8a5a..bea36529e 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/LocationRepository.kt +++ b/core/data/src/androidMain/kotlin/org/meshtastic/core/data/repository/LocationRepositoryImpl.kt @@ -20,6 +20,7 @@ import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION import android.app.Application import android.location.LocationManager +import android.os.Build import androidx.annotation.RequiresPermission import androidx.core.location.LocationCompat import androidx.core.location.LocationListenerCompat @@ -29,55 +30,61 @@ import androidx.core.location.altitude.AltitudeConverterCompat import co.touchlab.kermit.Logger import kotlinx.coroutines.asExecutor import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow -import org.meshtastic.core.analytics.platform.PlatformAnalytics import org.meshtastic.core.di.CoroutineDispatchers +import org.meshtastic.core.repository.Location +import org.meshtastic.core.repository.LocationRepository +import org.meshtastic.core.repository.PlatformAnalytics import javax.inject.Inject import javax.inject.Singleton @Singleton -class LocationRepository +class LocationRepositoryImpl @Inject constructor( private val context: Application, private val locationManager: dagger.Lazy, private val analytics: PlatformAnalytics, private val dispatchers: CoroutineDispatchers, -) { +) : LocationRepository { + + companion object { + private const val DEFAULT_INTERVAL_MS = 30_000L + private const val MIN_DISTANCE_METERS = 0f + private const val API_LEVEL_31 = 31 + } /** Status of whether the app is actively subscribed to location changes. */ private val _receivingLocationUpdates: MutableStateFlow = MutableStateFlow(false) - val receivingLocationUpdates: StateFlow + override val receivingLocationUpdates: StateFlow get() = _receivingLocationUpdates @RequiresPermission(anyOf = [ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION]) - private fun LocationManager.requestLocationUpdates() = callbackFlow { - val intervalMs = 30 * 1000L // 30 seconds - val minDistanceM = 0f - + private fun LocationManager.requestLocationUpdates(): Flow = callbackFlow { val locationRequest = - LocationRequestCompat.Builder(intervalMs) - .setMinUpdateDistanceMeters(minDistanceM) + LocationRequestCompat.Builder(DEFAULT_INTERVAL_MS) + .setMinUpdateDistanceMeters(MIN_DISTANCE_METERS) .setQuality(LocationRequestCompat.QUALITY_HIGH_ACCURACY) .build() val locationListener = LocationListenerCompat { location -> if (location.hasAltitude() && !LocationCompat.hasMslAltitude(location)) { + @Suppress("TooGenericExceptionCaught") try { AltitudeConverterCompat.addMslAltitudeToLocation(context, location) } catch (e: Exception) { Logger.e(e) { "addMslAltitudeToLocation() failed" } } } - // info("New location: $location") trySend(location) } val providerList = buildList { val providers = allProviders - if (android.os.Build.VERSION.SDK_INT >= 31 && LocationManager.FUSED_PROVIDER in providers) { + if (Build.VERSION.SDK_INT >= API_LEVEL_31 && LocationManager.FUSED_PROVIDER in providers) { add(LocationManager.FUSED_PROVIDER) } else { if (LocationManager.GPS_PROVIDER in providers) add(LocationManager.GPS_PROVIDER) @@ -86,11 +93,13 @@ constructor( } Logger.i { - "Starting location updates with $providerList intervalMs=${intervalMs}ms and minDistanceM=${minDistanceM}m" + "Starting location updates with $providerList intervalMs=$DEFAULT_INTERVAL_MS " + + "and minDistanceM=$MIN_DISTANCE_METERS" } _receivingLocationUpdates.value = true - analytics.track("location_start") // Figure out how many users needed to use the phone GPS + analytics.track("location_start") + @Suppress("TooGenericExceptionCaught") try { providerList.forEach { provider -> LocationManagerCompat.requestLocationUpdates( @@ -102,7 +111,7 @@ constructor( ) } } catch (e: Exception) { - close(e) // in case of exception, close the Flow + close(e) } awaitClose { @@ -116,5 +125,5 @@ constructor( /** Observable flow for location updates */ @RequiresPermission(anyOf = [ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION]) - fun getLocations() = locationManager.get().requestLocationUpdates() + override fun getLocations(): Flow = locationManager.get().requestLocationUpdates() } diff --git a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt new file mode 100644 index 000000000..db53ce59d --- /dev/null +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt @@ -0,0 +1,23 @@ +/* + * 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 . + */ +package org.meshtastic.core.data.datasource + +import org.meshtastic.core.model.BootloaderOtaQuirk + +interface BootloaderOtaQuirksJsonDataSource { + fun loadBootloaderOtaQuirksFromJsonAsset(): List +} diff --git a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt new file mode 100644 index 000000000..50d0ff89a --- /dev/null +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt @@ -0,0 +1,23 @@ +/* + * 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 . + */ +package org.meshtastic.core.data.datasource + +import org.meshtastic.core.model.NetworkDeviceHardware + +interface DeviceHardwareJsonDataSource { + fun loadDeviceHardwareFromJsonAsset(): List +} diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareLocalDataSource.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareLocalDataSource.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareLocalDataSource.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareLocalDataSource.kt diff --git a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt new file mode 100644 index 000000000..ceddabc0d --- /dev/null +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt @@ -0,0 +1,23 @@ +/* + * 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 . + */ +package org.meshtastic.core.data.datasource + +import org.meshtastic.core.model.NetworkFirmwareReleases + +interface FirmwareReleaseJsonDataSource { + fun loadFirmwareReleaseFromJsonAsset(): NetworkFirmwareReleases +} diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseLocalDataSource.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseLocalDataSource.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseLocalDataSource.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseLocalDataSource.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/NodeInfoReadDataSource.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/NodeInfoReadDataSource.kt similarity index 97% rename from core/data/src/main/kotlin/org/meshtastic/core/data/datasource/NodeInfoReadDataSource.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/NodeInfoReadDataSource.kt index 1e77cf25a..a01f6fc13 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/NodeInfoReadDataSource.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/NodeInfoReadDataSource.kt @@ -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,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.data.datasource import kotlinx.coroutines.flow.Flow diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/NodeInfoWriteDataSource.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/NodeInfoWriteDataSource.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/datasource/NodeInfoWriteDataSource.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/NodeInfoWriteDataSource.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoReadDataSource.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoReadDataSource.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoReadDataSource.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoReadDataSource.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoWriteDataSource.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoWriteDataSource.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoWriteDataSource.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoWriteDataSource.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt similarity index 96% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt index 4f262071c..c137ea8f6 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt @@ -17,6 +17,7 @@ package org.meshtastic.core.data.manager import co.touchlab.kermit.Logger +import kotlinx.atomicfu.atomic import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -45,12 +46,10 @@ import org.meshtastic.proto.Neighbor import org.meshtastic.proto.NeighborInfo import org.meshtastic.proto.PortNum import org.meshtastic.proto.Telemetry -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.atomic.AtomicLong -import java.util.concurrent.atomic.AtomicReference import javax.inject.Inject import javax.inject.Singleton import kotlin.math.absoluteValue +import kotlin.random.Random import kotlin.time.Duration.Companion.hours @Suppress("TooManyFunctions", "CyclomaticComplexMethod") @@ -63,10 +62,10 @@ constructor( private val radioConfigRepository: RadioConfigRepository, ) : CommandSender { private var scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - private val currentPacketId = AtomicLong(java.util.Random(nowMillis).nextLong().absoluteValue) - private val sessionPasskey = AtomicReference(ByteString.EMPTY) - override val tracerouteStartTimes = ConcurrentHashMap() - override val neighborInfoStartTimes = ConcurrentHashMap() + private val currentPacketId = atomic(Random(nowMillis).nextLong().absoluteValue) + private val sessionPasskey = atomic(ByteString.EMPTY) + override val tracerouteStartTimes = mutableMapOf() + override val neighborInfoStartTimes = mutableMapOf() private val localConfig = MutableStateFlow(LocalConfig()) private val channelSet = MutableStateFlow(ChannelSet()) @@ -87,7 +86,7 @@ constructor( override fun getCachedChannelSet(): ChannelSet = channelSet.value - override fun getCurrentPacketId(): Long = currentPacketId.get() + override fun getCurrentPacketId(): Long = currentPacketId.value override fun generatePacketId(): Int { val numPacketIds = ((1L shl PACKET_ID_SHIFT_BITS) - 1) @@ -96,7 +95,7 @@ constructor( } override fun setSessionPasskey(key: ByteString) { - sessionPasskey.set(key) + sessionPasskey.value = key } private fun computeHopLimit(): Int = (localConfig.value.lora?.hop_limit ?: 0).takeIf { it > 0 } ?: DEFAULT_HOP_LIMIT @@ -167,7 +166,7 @@ constructor( } override fun sendAdmin(destNum: Int, requestId: Int, wantResponse: Boolean, initFn: () -> AdminMessage) { - val adminMsg = initFn().copy(session_passkey = sessionPasskey.get()) + val adminMsg = initFn().copy(session_passkey = sessionPasskey.value) val packet = buildAdminPacket(to = destNum, id = requestId, wantResponse = wantResponse, adminMessage = adminMsg) packetHandler.sendToRadio(packet) diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/FromRadioPacketHandlerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/FromRadioPacketHandlerImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/FromRadioPacketHandlerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/FromRadioPacketHandlerImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/HistoryManagerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/HistoryManagerImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/HistoryManagerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/HistoryManagerImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshActionHandlerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshActionHandlerImpl.kt similarity index 99% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshActionHandlerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshActionHandlerImpl.kt index 07b30c0a7..f2a5e7c8b 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshActionHandlerImpl.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshActionHandlerImpl.kt @@ -21,8 +21,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import okio.ByteString.Companion.toByteString -import org.meshtastic.core.analytics.DataPair -import org.meshtastic.core.analytics.platform.PlatformAnalytics import org.meshtastic.core.common.database.DatabaseManager import org.meshtastic.core.common.util.handledLaunch import org.meshtastic.core.common.util.ignoreException @@ -34,6 +32,7 @@ import org.meshtastic.core.model.Position import org.meshtastic.core.model.Reaction import org.meshtastic.core.model.service.ServiceAction import org.meshtastic.core.repository.CommandSender +import org.meshtastic.core.repository.DataPair import org.meshtastic.core.repository.MeshActionHandler import org.meshtastic.core.repository.MeshDataHandler import org.meshtastic.core.repository.MeshMessageProcessor @@ -41,6 +40,7 @@ import org.meshtastic.core.repository.MeshPrefs import org.meshtastic.core.repository.MeshServiceNotifications import org.meshtastic.core.repository.NodeManager import org.meshtastic.core.repository.PacketRepository +import org.meshtastic.core.repository.PlatformAnalytics import org.meshtastic.core.repository.ServiceBroadcasts import org.meshtastic.proto.AdminMessage import org.meshtastic.proto.Channel diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshConfigFlowManagerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConfigFlowManagerImpl.kt similarity index 98% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshConfigFlowManagerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConfigFlowManagerImpl.kt index 86026b9be..d0daf20ed 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshConfigFlowManagerImpl.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConfigFlowManagerImpl.kt @@ -22,7 +22,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay -import org.meshtastic.core.analytics.platform.PlatformAnalytics +import okio.IOException import org.meshtastic.core.common.util.handledLaunch import org.meshtastic.core.model.ConnectionState import org.meshtastic.core.repository.CommandSender @@ -31,6 +31,7 @@ import org.meshtastic.core.repository.MeshConnectionManager import org.meshtastic.core.repository.NodeManager import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.PacketHandler +import org.meshtastic.core.repository.PlatformAnalytics import org.meshtastic.core.repository.RadioConfigRepository import org.meshtastic.core.repository.ServiceBroadcasts import org.meshtastic.core.repository.ServiceRepository @@ -39,7 +40,6 @@ import org.meshtastic.proto.HardwareModel import org.meshtastic.proto.Heartbeat import org.meshtastic.proto.NodeInfo import org.meshtastic.proto.ToRadio -import java.io.IOException import javax.inject.Inject import javax.inject.Singleton import org.meshtastic.core.model.MyNodeInfo as SharedMyNodeInfo diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshConfigHandlerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConfigHandlerImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshConfigHandlerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConfigHandlerImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImpl.kt similarity index 99% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImpl.kt index fbd87000c..eda76a0df 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImpl.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImpl.kt @@ -28,8 +28,6 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import org.meshtastic.core.analytics.DataPair -import org.meshtastic.core.analytics.platform.PlatformAnalytics import org.meshtastic.core.common.util.handledLaunch import org.meshtastic.core.common.util.nowMillis import org.meshtastic.core.common.util.nowSeconds @@ -37,6 +35,7 @@ import org.meshtastic.core.model.ConnectionState import org.meshtastic.core.model.TelemetryType import org.meshtastic.core.repository.AppWidgetUpdater import org.meshtastic.core.repository.CommandSender +import org.meshtastic.core.repository.DataPair import org.meshtastic.core.repository.HistoryManager import org.meshtastic.core.repository.MeshConnectionManager import org.meshtastic.core.repository.MeshLocationManager @@ -47,6 +46,7 @@ import org.meshtastic.core.repository.NodeManager import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.PacketHandler import org.meshtastic.core.repository.PacketRepository +import org.meshtastic.core.repository.PlatformAnalytics import org.meshtastic.core.repository.RadioConfigRepository import org.meshtastic.core.repository.RadioInterfaceService import org.meshtastic.core.repository.ServiceBroadcasts diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerImpl.kt similarity index 94% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerImpl.kt index 98b492c6a..ca8e3d01e 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerImpl.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerImpl.kt @@ -24,9 +24,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import okio.ByteString.Companion.toByteString -import org.meshtastic.core.analytics.DataPair -import org.meshtastic.core.analytics.platform.PlatformAnalytics +import okio.IOException import org.meshtastic.core.common.util.handledLaunch import org.meshtastic.core.common.util.nowMillis import org.meshtastic.core.common.util.nowSeconds @@ -39,6 +41,7 @@ import org.meshtastic.core.model.util.SfppHasher import org.meshtastic.core.model.util.decodeOrNull import org.meshtastic.core.model.util.toOneLiner import org.meshtastic.core.repository.CommandSender +import org.meshtastic.core.repository.DataPair import org.meshtastic.core.repository.HistoryManager import org.meshtastic.core.repository.MeshConfigFlowManager import org.meshtastic.core.repository.MeshConfigHandler @@ -50,6 +53,7 @@ import org.meshtastic.core.repository.NeighborInfoHandler import org.meshtastic.core.repository.NodeManager import org.meshtastic.core.repository.PacketHandler import org.meshtastic.core.repository.PacketRepository +import org.meshtastic.core.repository.PlatformAnalytics import org.meshtastic.core.repository.RadioConfigRepository import org.meshtastic.core.repository.ServiceBroadcasts import org.meshtastic.core.repository.ServiceRepository @@ -72,8 +76,6 @@ import org.meshtastic.proto.StoreForwardPlusPlus import org.meshtastic.proto.Telemetry import org.meshtastic.proto.User import org.meshtastic.proto.Waypoint -import java.io.IOException -import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject import javax.inject.Singleton import kotlin.time.Duration.Companion.milliseconds @@ -355,11 +357,11 @@ constructor( u.session_passkey.let { commandSender.setSessionPasskey(it) } val fromNum = packet.from - u.get_module_config_response?.let { config -> + u.get_module_config_response?.let { if (fromNum == myNodeNum) { - configHandler.get().handleModuleConfig(config) + configHandler.get().handleModuleConfig(it) } else { - config.statusmessage?.node_status?.let { nodeManager.updateNodeStatus(fromNum, it) } + it.statusmessage?.node_status?.let { nodeManager.updateNodeStatus(fromNum, it) } } } @@ -368,11 +370,11 @@ constructor( u.get_channel_response?.let { configHandler.get().handleChannel(it) } } - u.get_device_metadata_response?.let { metadata -> + u.get_device_metadata_response?.let { if (fromNum == myNodeNum) { - configFlowManager.get().handleLocalMetadata(metadata) + configFlowManager.get().handleLocalMetadata(it) } else { - nodeManager.insertMetadata(fromNum, metadata) + nodeManager.insertMetadata(fromNum, it) } } } @@ -429,14 +431,20 @@ constructor( (metrics.voltage ?: 0f) > BATTERY_PERCENT_UNSUPPORTED && (metrics.battery_level ?: 0) <= BATTERY_PERCENT_LOW_THRESHOLD ) { - if (shouldBatteryNotificationShow(fromNum, t, myNodeNum)) { - serviceNotifications.showOrUpdateLowBatteryNotification(nextNode, isRemote) + scope.launch { + if (shouldBatteryNotificationShow(fromNum, t, myNodeNum)) { + serviceNotifications.showOrUpdateLowBatteryNotification(nextNode, isRemote) + } } } else { - if (batteryPercentCooldowns.containsKey(fromNum)) { - batteryPercentCooldowns.remove(fromNum) + scope.launch { + batteryMutex.withLock { + if (batteryPercentCooldowns.containsKey(fromNum)) { + batteryPercentCooldowns.remove(fromNum) + } + } + serviceNotifications.cancelLowBatteryNotification(nextNode) } - serviceNotifications.cancelLowBatteryNotification(nextNode) } } } @@ -451,7 +459,7 @@ constructor( } @Suppress("ReturnCount") - private fun shouldBatteryNotificationShow(fromNum: Int, t: Telemetry, myNodeNum: Int): Boolean { + private suspend fun shouldBatteryNotificationShow(fromNum: Int, t: Telemetry, myNodeNum: Int): Boolean { val isRemote = (fromNum != myNodeNum) var shouldDisplay = false var forceDisplay = false @@ -470,10 +478,12 @@ constructor( } if (shouldDisplay) { val now = nowSeconds - if (!batteryPercentCooldowns.containsKey(fromNum)) batteryPercentCooldowns[fromNum] = 0L - if ((now - batteryPercentCooldowns[fromNum]!!) >= BATTERY_PERCENT_COOLDOWN_SECONDS || forceDisplay) { - batteryPercentCooldowns[fromNum] = now - return true + batteryMutex.withLock { + if (!batteryPercentCooldowns.containsKey(fromNum)) batteryPercentCooldowns[fromNum] = 0L + if ((now - batteryPercentCooldowns[fromNum]!!) >= BATTERY_PERCENT_COOLDOWN_SECONDS || forceDisplay) { + batteryPercentCooldowns[fromNum] = now + return true + } } } return false @@ -775,6 +785,7 @@ constructor( private const val BATTERY_PERCENT_LOW_DIVISOR = 5 private const val BATTERY_PERCENT_CRITICAL_THRESHOLD = 5 private const val BATTERY_PERCENT_COOLDOWN_SECONDS = 1500 - private val batteryPercentCooldowns = ConcurrentHashMap() + private val batteryMutex = Mutex() + private val batteryPercentCooldowns = mutableMapOf() } } diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshMessageProcessorImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshMessageProcessorImpl.kt similarity index 81% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshMessageProcessorImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshMessageProcessorImpl.kt index cda802c89..5ba3605c4 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshMessageProcessorImpl.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshMessageProcessorImpl.kt @@ -24,6 +24,9 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import org.meshtastic.core.common.util.handledLaunch import org.meshtastic.core.common.util.nowMillis import org.meshtastic.core.common.util.nowSeconds @@ -40,9 +43,6 @@ import org.meshtastic.proto.FromRadio import org.meshtastic.proto.LogRecord import org.meshtastic.proto.MeshPacket import org.meshtastic.proto.PortNum -import java.util.ArrayDeque -import java.util.Locale -import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject import javax.inject.Singleton import kotlin.uuid.Uuid @@ -60,14 +60,17 @@ constructor( private val fromRadioDispatcher: FromRadioPacketHandler, ) : MeshMessageProcessor { private var scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - private val logUuidByPacketId = ConcurrentHashMap() - private val logInsertJobByPacketId = ConcurrentHashMap() - private val earlyReceivedPackets = ArrayDeque() + private val mapsMutex = Mutex() + private val logUuidByPacketId = mutableMapOf() + private val logInsertJobByPacketId = mutableMapOf() + + private val earlyMutex = Mutex() + private val earlyReceivedPackets = kotlin.collections.ArrayDeque() private val maxEarlyPacketBuffer = 10240 override fun clearEarlyPackets() { - synchronized(earlyReceivedPackets) { earlyReceivedPackets.clear() } + scope.launch { earlyMutex.withLock { earlyReceivedPackets.clear() } } } override fun start(scope: CoroutineScope) { @@ -91,8 +94,7 @@ constructor( } .onFailure { _ -> Logger.e(primaryException) { - "Failed to parse radio packet (len=${bytes.size} contents=${bytes.toHexString()}). " + - "Not a valid FromRadio or LogRecord." + "Failed to parse radio packet (len=${bytes.size}). " + "Not a valid FromRadio or LogRecord." } } } @@ -150,27 +152,33 @@ constructor( if (nodeManager.isNodeDbReady.value) { processReceivedMeshPacket(preparedPacket, myNodeNum) } else { - synchronized(earlyReceivedPackets) { - val queueSize = earlyReceivedPackets.size - if (queueSize >= maxEarlyPacketBuffer) { - earlyReceivedPackets.removeFirst() + scope.launch { + earlyMutex.withLock { + val queueSize = earlyReceivedPackets.size + if (queueSize >= maxEarlyPacketBuffer) { + earlyReceivedPackets.removeFirstOrNull() + } + earlyReceivedPackets.addLast(preparedPacket) } - earlyReceivedPackets.addLast(preparedPacket) } } } private fun flushEarlyReceivedPackets(reason: String) { - val packets = - synchronized(earlyReceivedPackets) { - if (earlyReceivedPackets.isEmpty()) return - val list = earlyReceivedPackets.toList() - earlyReceivedPackets.clear() - list - } - Logger.d { "replayEarlyPackets reason=$reason count=${packets.size}" } - val myNodeNum = nodeManager.myNodeNum - packets.forEach { processReceivedMeshPacket(it, myNodeNum) } + scope.launch { + val packets = + earlyMutex.withLock { + if (earlyReceivedPackets.isEmpty()) return@withLock emptyList() + val list = earlyReceivedPackets.toList() + earlyReceivedPackets.clear() + list + } + if (packets.isEmpty()) return@launch + + Logger.d { "replayEarlyPackets reason=$reason count=${packets.size}" } + val myNodeNum = nodeManager.myNodeNum + packets.forEach { processReceivedMeshPacket(it, myNodeNum) } + } } @Suppress("LongMethod") @@ -187,8 +195,13 @@ constructor( fromRadio = FromRadio(packet = packet), ) val logJob = insertMeshLog(log) - logInsertJobByPacketId[packet.id] = logJob - logUuidByPacketId[packet.id] = log.uuid + + scope.launch { + mapsMutex.withLock { + logInsertJobByPacketId[packet.id] = logJob + logUuidByPacketId[packet.id] = log.uuid + } + } scope.handledLaunch { serviceRepository.emitMeshPacket(packet) } @@ -235,14 +248,15 @@ constructor( try { router.get().dataHandler.handleReceivedData(packet, myNum, log.uuid, logJob) } finally { - logUuidByPacketId.remove(packet.id) - logInsertJobByPacketId.remove(packet.id) + scope.launch { + mapsMutex.withLock { + logUuidByPacketId.remove(packet.id) + logInsertJobByPacketId.remove(packet.id) + } + } } } } private fun insertMeshLog(log: MeshLog): Job = scope.handledLaunch { meshLogRepository.get().insert(log) } - - private fun ByteArray.toHexString(): String = - this.joinToString(",") { byte -> String.format(Locale.US, "0x%02x", byte) } } diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshRouterImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshRouterImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshRouterImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshRouterImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MessageFilterImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MessageFilterImpl.kt similarity index 95% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/MessageFilterImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MessageFilterImpl.kt index a907c9a9f..17e7c5091 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MessageFilterImpl.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MessageFilterImpl.kt @@ -19,7 +19,6 @@ package org.meshtastic.core.data.manager import co.touchlab.kermit.Logger import org.meshtastic.core.repository.FilterPrefs import org.meshtastic.core.repository.MessageFilter -import java.util.regex.PatternSyntaxException import javax.inject.Inject import javax.inject.Singleton @@ -49,7 +48,7 @@ class MessageFilterImpl @Inject constructor(private val filterPrefs: FilterPrefs } else { Regex("\\b${Regex.escape(word)}\\b", RegexOption.IGNORE_CASE) } - } catch (e: PatternSyntaxException) { + } catch (e: IllegalArgumentException) { Logger.w { "Invalid filter pattern: $word - ${e.message}" } null } diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/MqttManagerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MqttManagerImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/MqttManagerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MqttManagerImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/NeighborInfoHandlerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/NeighborInfoHandlerImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/NeighborInfoHandlerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/NeighborInfoHandlerImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/NodeManagerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/NodeManagerImpl.kt similarity index 82% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/NodeManagerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/NodeManagerImpl.kt index 6f9c615d5..120d79b08 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/NodeManagerImpl.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/NodeManagerImpl.kt @@ -17,6 +17,9 @@ package org.meshtastic.core.data.manager import co.touchlab.kermit.Logger +import kotlinx.atomicfu.atomic +import kotlinx.atomicfu.update +import kotlinx.collections.immutable.persistentMapOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -42,21 +45,12 @@ import org.meshtastic.proto.Paxcount import org.meshtastic.proto.StatusMessage import org.meshtastic.proto.Telemetry import org.meshtastic.proto.User -import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject import javax.inject.Singleton import org.meshtastic.proto.NodeInfo as ProtoNodeInfo import org.meshtastic.proto.Position as ProtoPosition -/** - * Implementation of [NodeManager] that maintains an in-memory database of the mesh. - * - * This component acts as the "brain" for node-related data during a connection session. It manages: - * 1. In-memory maps for fast node lookup by number or ID. - * 2. Synchronization of node data between the radio and the persistent database. - * 3. Processing of incoming node-related packets (User, Position, Telemetry). - * 4. Broadcasting changes to the rest of the application. - */ +/** Implementation of [NodeManager] that maintains an in-memory database of the mesh. */ @Suppress("LongParameterList", "TooManyFunctions", "CyclomaticComplexMethod") @Singleton class NodeManagerImpl @@ -68,8 +62,14 @@ constructor( ) : NodeManager { private var scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - override val nodeDBbyNodeNum = ConcurrentHashMap() - override val nodeDBbyID = ConcurrentHashMap() + private val _nodeDBbyNodeNum = atomic(persistentMapOf()) + private val _nodeDBbyID = atomic(persistentMapOf()) + + override val nodeDBbyNodeNum: Map + get() = _nodeDBbyNodeNum.value + + override val nodeDBbyID: Map + get() = _nodeDBbyID.value override val isNodeDbReady = MutableStateFlow(false) override val allowNodeDbWrites = MutableStateFlow(false) @@ -95,15 +95,17 @@ constructor( override fun loadCachedNodeDB() { scope.handledLaunch { val nodes = nodeRepository.nodeDBbyNum.first() - nodeDBbyNodeNum.putAll(nodes) - nodes.values.forEach { nodeDBbyID[it.user.id] = it } + _nodeDBbyNodeNum.value = persistentMapOf().putAll(nodes) + val byId = mutableMapOf() + nodes.values.forEach { byId[it.user.id] = it } + _nodeDBbyID.value = persistentMapOf().putAll(byId) myNodeNum = nodeRepository.myNodeInfo.value?.myNodeNum } } override fun clear() { - nodeDBbyNodeNum.clear() - nodeDBbyID.clear() + _nodeDBbyNodeNum.value = persistentMapOf() + _nodeDBbyID.value = persistentMapOf() isNodeDbReady.value = false allowNodeDbWrites.value = false myNodeNum = null @@ -111,7 +113,7 @@ constructor( override fun getMyNodeInfo(): MyNodeInfo? { val mi = nodeRepository.myNodeInfo.value ?: return null - val myNode = nodeDBbyNodeNum[mi.myNodeNum] + val myNode = _nodeDBbyNodeNum.value[mi.myNodeNum] return MyNodeInfo( myNodeNum = mi.myNodeNum, hasGPS = (myNode?.position?.latitude_i ?: 0) != 0, @@ -132,34 +134,41 @@ constructor( override fun getMyId(): String { val num = myNodeNum ?: nodeRepository.myNodeInfo.value?.myNodeNum ?: return "" - return nodeDBbyNodeNum[num]?.user?.id ?: "" + return _nodeDBbyNodeNum.value[num]?.user?.id ?: "" } - override fun getNodes(): List = nodeDBbyNodeNum.values.map { it.toNodeInfo() } + override fun getNodes(): List = _nodeDBbyNodeNum.value.values.map { it.toNodeInfo() } override fun removeByNodenum(nodeNum: Int) { - nodeDBbyNodeNum.remove(nodeNum)?.let { nodeDBbyID.remove(it.user.id) } + val removed = atomic(null) + _nodeDBbyNodeNum.update { map -> + val node = map[nodeNum] + removed.value = node + map.remove(nodeNum) + } + removed.value?.let { node -> _nodeDBbyID.update { it.remove(node.user.id) } } } - fun getOrCreateNode(n: Int, channel: Int = 0): Node = nodeDBbyNodeNum.getOrPut(n) { - val userId = DataPacket.nodeNumToDefaultId(n) - val defaultUser = - User( - id = userId, - long_name = "Meshtastic ${userId.takeLast(n = 4)}", - short_name = userId.takeLast(n = 4), - hw_model = HardwareModel.UNSET, - ) + internal fun getOrCreateNode(n: Int, channel: Int = 0): Node = _nodeDBbyNodeNum.value[n] + ?: run { + val userId = DataPacket.nodeNumToDefaultId(n) + val defaultUser = + User( + id = userId, + long_name = "Meshtastic ${userId.takeLast(n = 4)}", + short_name = userId.takeLast(n = 4), + hw_model = HardwareModel.UNSET, + ) - Node(num = n, user = defaultUser, channel = channel) - } + Node(num = n, user = defaultUser, channel = channel) + } override fun updateNode(nodeNum: Int, withBroadcast: Boolean, channel: Int, transform: (Node) -> Node) { - val current = nodeDBbyNodeNum[nodeNum] ?: getOrCreateNode(nodeNum, channel) - val next = transform(current) - nodeDBbyNodeNum[nodeNum] = next + val next = transform(_nodeDBbyNodeNum.value[nodeNum] ?: getOrCreateNode(nodeNum, channel)) + + _nodeDBbyNodeNum.update { it.put(nodeNum, next) } if (next.user.id.isNotEmpty()) { - nodeDBbyID[next.user.id] = next + _nodeDBbyID.update { it.put(next.user.id, next) } } if (next.user.id.isNotEmpty() && isNodeDbReady.value) { @@ -252,7 +261,8 @@ constructor( if (shouldPreserveExistingUser(node.user, user)) { // keep existing names } else { - var newUser = user.let { if (it.is_licensed) it.copy(public_key = ByteString.EMPTY) else it } + var newUser = + user.let { if (it.is_licensed == true) it.copy(public_key = ByteString.EMPTY) else it } if (info.via_mqtt) { newUser = newUser.copy(long_name = "${newUser.long_name} (MQTT)") } @@ -292,7 +302,7 @@ constructor( override fun toNodeID(nodeNum: Int): String = if (nodeNum == DataPacket.NODENUM_BROADCAST) { DataPacket.ID_BROADCAST } else { - nodeDBbyNodeNum[nodeNum]?.user?.id ?: DataPacket.nodeNumToDefaultId(nodeNum) + _nodeDBbyNodeNum.value[nodeNum]?.user?.id ?: DataPacket.nodeNumToDefaultId(nodeNum) } private fun Node.toNodeInfo(): NodeInfo = NodeInfo( diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/PacketHandlerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/PacketHandlerImpl.kt similarity index 70% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/PacketHandlerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/PacketHandlerImpl.kt index a42e77810..a3f31f448 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/PacketHandlerImpl.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/PacketHandlerImpl.kt @@ -24,6 +24,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeoutOrNull import org.meshtastic.core.common.util.handledLaunch @@ -45,8 +48,6 @@ import org.meshtastic.proto.FromRadio import org.meshtastic.proto.MeshPacket import org.meshtastic.proto.QueueStatus import org.meshtastic.proto.ToRadio -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.ConcurrentLinkedQueue import javax.inject.Inject import javax.inject.Singleton import kotlin.time.Duration.Companion.milliseconds @@ -72,8 +73,11 @@ constructor( private var queueJob: Job? = null private var scope: CoroutineScope = CoroutineScope(Dispatchers.IO) - private val queuedPackets = ConcurrentLinkedQueue() - private val queueResponse = ConcurrentHashMap>() + private val queueMutex = Mutex() + private val queuedPackets = mutableListOf() + + private val responseMutex = Mutex() + private val queueResponse = mutableMapOf>() override fun start(scope: CoroutineScope) { this.scope = scope @@ -103,8 +107,10 @@ constructor( } override fun sendToRadio(packet: MeshPacket) { - queuedPackets.add(packet) - startPacketQueue() + scope.launch { + queueMutex.withLock { queuedPackets.add(packet) } + startPacketQueue() + } } override fun stopPacketQueue() { @@ -112,9 +118,13 @@ constructor( Logger.i { "Stopping packet queueJob" } queueJob?.cancel() queueJob = null - queuedPackets.clear() - queueResponse.entries.lastOrNull { !it.value.isCompleted }?.value?.complete(false) - queueResponse.clear() + scope.launch { + queueMutex.withLock { queuedPackets.clear() } + responseMutex.withLock { + queueResponse.values.lastOrNull { !it.isCompleted }?.complete(false) + queueResponse.clear() + } + } } } @@ -122,15 +132,20 @@ constructor( Logger.d { "[queueStatus] ${queueStatus.toOneLineString()}" } val (success, isFull, requestId) = with(queueStatus) { Triple(res == 0, free == 0, mesh_packet_id) } if (success && isFull) return - if (requestId != 0) { - queueResponse.remove(requestId)?.complete(success) - } else { - queueResponse.values.firstOrNull { !it.isCompleted }?.complete(success) + + scope.launch { + responseMutex.withLock { + if (requestId != 0) { + queueResponse.remove(requestId)?.complete(success) + } else { + queueResponse.values.firstOrNull { !it.isCompleted }?.complete(success) + } + } } } override fun removeResponse(dataRequestId: Int, complete: Boolean) { - queueResponse.remove(dataRequestId)?.complete(complete) + scope.launch { responseMutex.withLock { queueResponse.remove(dataRequestId)?.complete(complete) } } } private fun startPacketQueue() { @@ -138,20 +153,27 @@ constructor( queueJob = scope.handledLaunch { Logger.d { "packet queueJob started" } - while (serviceRepository.connectionState.value == ConnectionState.Connected) { - val packet = queuedPackets.poll() ?: break - @Suppress("TooGenericExceptionCaught", "SwallowedException") - try { - val response = sendPacket(packet) - Logger.d { "queueJob packet id=${packet.id.toUInt()} waiting" } - val success = withTimeout(TIMEOUT) { response.await() } - Logger.d { "queueJob packet id=${packet.id.toUInt()} success $success" } - } catch (e: TimeoutCancellationException) { - Logger.d { "queueJob packet id=${packet.id.toUInt()} timeout" } - } catch (e: Exception) { - Logger.d { "queueJob packet id=${packet.id.toUInt()} failed" } - } finally { - queueResponse.remove(packet.id) + try { + while (serviceRepository.connectionState.value == ConnectionState.Connected) { + val packet = queueMutex.withLock { queuedPackets.removeFirstOrNull() } ?: break + @Suppress("TooGenericExceptionCaught", "SwallowedException") + try { + val response = sendPacket(packet) + Logger.d { "queueJob packet id=${packet.id.toUInt()} waiting" } + val success = withTimeout(TIMEOUT) { response.await() } + Logger.d { "queueJob packet id=${packet.id.toUInt()} success $success" } + } catch (e: TimeoutCancellationException) { + Logger.d { "queueJob packet id=${packet.id.toUInt()} timeout" } + } catch (e: Exception) { + Logger.d { "queueJob packet id=${packet.id.toUInt()} failed" } + } finally { + responseMutex.withLock { queueResponse.remove(packet.id) } + } + } + } finally { + queueJob = null + if (queueMutex.withLock { queuedPackets.isNotEmpty() }) { + startPacketQueue() } } } @@ -177,9 +199,9 @@ constructor( } @Suppress("TooGenericExceptionCaught") - private fun sendPacket(packet: MeshPacket): CompletableDeferred { + private suspend fun sendPacket(packet: MeshPacket): CompletableDeferred { val deferred = CompletableDeferred() - queueResponse[packet.id] = deferred + responseMutex.withLock { queueResponse[packet.id] = deferred } try { if (serviceRepository.connectionState.value != ConnectionState.Connected) { throw RadioNotConnectedException() diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/manager/TracerouteHandlerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/TracerouteHandlerImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/manager/TracerouteHandlerImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/TracerouteHandlerImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepositoryImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepositoryImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepositoryImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepositoryImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/NodeRepositoryImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/NodeRepositoryImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/repository/NodeRepositoryImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/NodeRepositoryImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/PacketRepositoryImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/PacketRepositoryImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/repository/PacketRepositoryImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/PacketRepositoryImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/QuickChatActionRepository.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/QuickChatActionRepository.kt similarity index 97% rename from core/data/src/main/kotlin/org/meshtastic/core/data/repository/QuickChatActionRepository.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/QuickChatActionRepository.kt index 0d58d6b7f..025518f86 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/QuickChatActionRepository.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/QuickChatActionRepository.kt @@ -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,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.data.repository import kotlinx.coroutines.flow.flatMapLatest diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/RadioConfigRepositoryImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/RadioConfigRepositoryImpl.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/repository/RadioConfigRepositoryImpl.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/RadioConfigRepositoryImpl.kt diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/TracerouteSnapshotRepository.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/TracerouteSnapshotRepository.kt similarity index 100% rename from core/data/src/main/kotlin/org/meshtastic/core/data/repository/TracerouteSnapshotRepository.kt rename to core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/TracerouteSnapshotRepository.kt diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/manager/CommandSenderHopLimitTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/CommandSenderHopLimitTest.kt similarity index 100% rename from core/data/src/test/kotlin/org/meshtastic/core/data/manager/CommandSenderHopLimitTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/CommandSenderHopLimitTest.kt diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/manager/CommandSenderImplTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/CommandSenderImplTest.kt similarity index 100% rename from core/data/src/test/kotlin/org/meshtastic/core/data/manager/CommandSenderImplTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/CommandSenderImplTest.kt diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/manager/FromRadioPacketHandlerImplTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/FromRadioPacketHandlerImplTest.kt similarity index 100% rename from core/data/src/test/kotlin/org/meshtastic/core/data/manager/FromRadioPacketHandlerImplTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/FromRadioPacketHandlerImplTest.kt diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/manager/HistoryManagerImplTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/HistoryManagerImplTest.kt similarity index 100% rename from core/data/src/test/kotlin/org/meshtastic/core/data/manager/HistoryManagerImplTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/HistoryManagerImplTest.kt diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImplTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImplTest.kt similarity index 99% rename from core/data/src/test/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImplTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImplTest.kt index 258756e9c..c62549e9a 100644 --- a/core/data/src/test/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImplTest.kt +++ b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImplTest.kt @@ -31,7 +31,6 @@ import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test -import org.meshtastic.core.analytics.platform.PlatformAnalytics import org.meshtastic.core.model.ConnectionState import org.meshtastic.core.model.DataPacket import org.meshtastic.core.model.MyNodeInfo @@ -47,6 +46,7 @@ import org.meshtastic.core.repository.NodeManager import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.PacketHandler import org.meshtastic.core.repository.PacketRepository +import org.meshtastic.core.repository.PlatformAnalytics import org.meshtastic.core.repository.RadioConfigRepository import org.meshtastic.core.repository.RadioInterfaceService import org.meshtastic.core.repository.ServiceBroadcasts diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerTest.kt similarity index 99% rename from core/data/src/test/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerTest.kt index 0c133b36f..b4eb95f9d 100644 --- a/core/data/src/test/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerTest.kt +++ b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/MeshDataHandlerTest.kt @@ -28,7 +28,6 @@ import kotlinx.coroutines.test.runTest import okio.ByteString.Companion.toByteString import org.junit.Before import org.junit.Test -import org.meshtastic.core.analytics.platform.PlatformAnalytics import org.meshtastic.core.model.DataPacket import org.meshtastic.core.model.MessageStatus import org.meshtastic.core.model.util.MeshDataMapper @@ -43,6 +42,7 @@ import org.meshtastic.core.repository.NeighborInfoHandler import org.meshtastic.core.repository.NodeManager import org.meshtastic.core.repository.PacketHandler import org.meshtastic.core.repository.PacketRepository +import org.meshtastic.core.repository.PlatformAnalytics import org.meshtastic.core.repository.RadioConfigRepository import org.meshtastic.core.repository.ServiceBroadcasts import org.meshtastic.core.repository.ServiceRepository diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/manager/MessageFilterImplTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/MessageFilterImplTest.kt similarity index 100% rename from core/data/src/test/kotlin/org/meshtastic/core/data/manager/MessageFilterImplTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/MessageFilterImplTest.kt diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/manager/NodeManagerImplTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/NodeManagerImplTest.kt similarity index 100% rename from core/data/src/test/kotlin/org/meshtastic/core/data/manager/NodeManagerImplTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/NodeManagerImplTest.kt diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/manager/PacketHandlerImplTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/PacketHandlerImplTest.kt similarity index 100% rename from core/data/src/test/kotlin/org/meshtastic/core/data/manager/PacketHandlerImplTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/PacketHandlerImplTest.kt diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepositoryTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepositoryTest.kt similarity index 100% rename from core/data/src/test/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepositoryTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepositoryTest.kt diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryTest.kt similarity index 100% rename from core/data/src/test/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryTest.kt diff --git a/core/data/src/test/kotlin/org/meshtastic/core/data/repository/NodeRepositoryTest.kt b/core/data/src/commonTest/kotlin/org/meshtastic/core/data/repository/NodeRepositoryTest.kt similarity index 100% rename from core/data/src/test/kotlin/org/meshtastic/core/data/repository/NodeRepositoryTest.kt rename to core/data/src/commonTest/kotlin/org/meshtastic/core/data/repository/NodeRepositoryTest.kt diff --git a/core/data/src/google/kotlin/org/meshtastic/core/data/di/GoogleDataModule.kt b/core/data/src/google/kotlin/org/meshtastic/core/data/di/GoogleDataModule.kt deleted file mode 100644 index 391a39d96..000000000 --- a/core/data/src/google/kotlin/org/meshtastic/core/data/di/GoogleDataModule.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2025 Meshtastic LLC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.meshtastic.core.data.di - -import dagger.Binds -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import kotlinx.serialization.json.Json -import org.meshtastic.core.data.repository.CustomTileProviderRepository -import org.meshtastic.core.data.repository.CustomTileProviderRepositoryImpl -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -interface GoogleDataModule { - - @Binds - @Singleton - fun bindCustomTileProviderRepository(impl: CustomTileProviderRepositoryImpl): CustomTileProviderRepository - - companion object { - @Provides @Singleton - fun provideJson(): Json = Json { prettyPrint = false } - } -} diff --git a/core/database/detekt-baseline.xml b/core/database/detekt-baseline.xml index b6b5c743a..c373eea43 100644 --- a/core/database/detekt-baseline.xml +++ b/core/database/detekt-baseline.xml @@ -1,8 +1,5 @@ - - CyclomaticComplexMethod:Node.kt$Node$private fun EnvironmentMetrics.getDisplayStrings(isFahrenheit: Boolean): List<String> - TooGenericExceptionCaught:Converters.kt$Converters$ex: Exception - + diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts index 874153009..f94dc4779 100644 --- a/core/datastore/build.gradle.kts +++ b/core/datastore/build.gradle.kts @@ -27,15 +27,13 @@ kotlin { sourceSets { commonMain.dependencies { implementation(projects.core.proto) - implementation(libs.androidx.datastore) - implementation(libs.androidx.datastore.preferences) + api(libs.androidx.datastore) + api(libs.androidx.datastore.preferences) + api(libs.javax.inject) implementation(libs.kotlinx.serialization.json) implementation(libs.kermit) } - androidMain.dependencies { - implementation(libs.hilt.android) - implementation(libs.javax.inject) - } + androidMain.dependencies { implementation(libs.hilt.android) } } } diff --git a/core/di/build.gradle.kts b/core/di/build.gradle.kts index d968dda63..59f82dbeb 100644 --- a/core/di/build.gradle.kts +++ b/core/di/build.gradle.kts @@ -14,30 +14,20 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import com.android.build.api.dsl.LibraryExtension -/* - * Copyright (c) 2025 Meshtastic LLC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ +plugins { alias(libs.plugins.meshtastic.kmp.library) } -plugins { - alias(libs.plugins.meshtastic.android.library) - alias(libs.plugins.meshtastic.hilt) +kotlin { + @Suppress("UnstableApiUsage") + android { + namespace = "org.meshtastic.core.di" + androidResources.enable = false + } + + sourceSets { + commonMain.dependencies { + api(libs.javax.inject) + implementation(libs.kotlinx.coroutines.core) + } + } } - -configure { namespace = "org.meshtastic.core.di" } - -dependencies { implementation(libs.androidx.work.runtime.ktx) } diff --git a/core/di/src/main/kotlin/org/meshtastic/core/di/CoroutineDispatchers.kt b/core/di/src/commonMain/kotlin/org/meshtastic/core/di/CoroutineDispatchers.kt similarity index 95% rename from core/di/src/main/kotlin/org/meshtastic/core/di/CoroutineDispatchers.kt rename to core/di/src/commonMain/kotlin/org/meshtastic/core/di/CoroutineDispatchers.kt index a7d4ad92c..381c17e1a 100644 --- a/core/di/src/main/kotlin/org/meshtastic/core/di/CoroutineDispatchers.kt +++ b/core/di/src/commonMain/kotlin/org/meshtastic/core/di/CoroutineDispatchers.kt @@ -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,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.di import kotlinx.coroutines.CoroutineDispatcher diff --git a/core/di/src/main/kotlin/org/meshtastic/core/di/ProcessLifecycle.kt b/core/di/src/commonMain/kotlin/org/meshtastic/core/di/ProcessLifecycle.kt similarity index 95% rename from core/di/src/main/kotlin/org/meshtastic/core/di/ProcessLifecycle.kt rename to core/di/src/commonMain/kotlin/org/meshtastic/core/di/ProcessLifecycle.kt index 76311b20a..5eb0b500c 100644 --- a/core/di/src/main/kotlin/org/meshtastic/core/di/ProcessLifecycle.kt +++ b/core/di/src/commonMain/kotlin/org/meshtastic/core/di/ProcessLifecycle.kt @@ -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,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.di import javax.inject.Qualifier diff --git a/core/domain/build.gradle.kts b/core/domain/build.gradle.kts index d78eb1c6c..64c8fd8f5 100644 --- a/core/domain/build.gradle.kts +++ b/core/domain/build.gradle.kts @@ -16,28 +16,42 @@ */ plugins { - alias(libs.plugins.meshtastic.android.library) - alias(libs.plugins.meshtastic.android.library.flavors) - alias(libs.plugins.meshtastic.hilt) + alias(libs.plugins.meshtastic.kmp.library) + alias(libs.plugins.devtools.ksp) } -android { namespace = "org.meshtastic.core.domain" } +kotlin { + @Suppress("UnstableApiUsage") + android { + namespace = "org.meshtastic.core.domain" + androidResources.enable = false + withHostTest { isIncludeAndroidResources = true } + } -dependencies { - implementation(projects.core.repository) - implementation(projects.core.model) - implementation(projects.core.proto) - implementation(projects.core.common) - implementation(projects.core.database) - implementation(projects.core.datastore) - implementation(projects.core.resources) + sourceSets { + commonMain.dependencies { + implementation(projects.core.repository) + implementation(projects.core.model) + implementation(projects.core.proto) + implementation(projects.core.common) + implementation(projects.core.database) + implementation(projects.core.datastore) + implementation(projects.core.resources) - implementation(libs.kermit) - implementation(libs.compose.multiplatform.resources) - - testImplementation(libs.junit) - testImplementation(libs.mockk) - testImplementation(libs.robolectric) - testImplementation(libs.turbine) - testImplementation(libs.kotlinx.coroutines.test) + api(libs.javax.inject) + implementation(libs.kermit) + implementation(libs.compose.multiplatform.resources) + implementation(libs.okio) + implementation(libs.kotlinx.datetime) + implementation(libs.kotlinx.serialization.json) + } + commonTest.dependencies { + implementation(kotlin("test")) + implementation(libs.kotlinx.coroutines.test) + implementation(libs.turbine) + implementation(libs.mockk) + } + } } + +dependencies { add("kspAndroid", libs.hilt.compiler) } diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCase.kt similarity index 85% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCase.kt index ce7261863..6897f4c9f 100644 --- a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCase.kt +++ b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCase.kt @@ -16,15 +16,16 @@ */ package org.meshtastic.core.domain.usecase.settings -import android.icu.text.SimpleDateFormat import kotlinx.coroutines.flow.first +import kotlinx.datetime.Instant +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime +import okio.BufferedSink import org.meshtastic.core.model.Position import org.meshtastic.core.model.util.positionToMeter import org.meshtastic.core.repository.MeshLogRepository import org.meshtastic.core.repository.NodeRepository import org.meshtastic.proto.PortNum -import java.io.BufferedWriter -import java.util.Locale import javax.inject.Inject import kotlin.math.roundToInt import org.meshtastic.proto.Position as ProtoPosition @@ -37,14 +38,14 @@ constructor( private val meshLogRepository: MeshLogRepository, ) { /** - * Writes all persisted packet data to the provided [BufferedWriter]. + * Writes all persisted packet data to the provided [BufferedSink]. * - * @param writer The writer to output the CSV data to. + * @param sink The sink to output the CSV data to. * @param myNodeNum The node number of the current device. * @param filterPortnum If provided, only packets with this port number will be exported. */ @Suppress("detekt:CyclomaticComplexMethod", "detekt:LongMethod", "detekt:NestedBlockDepth") - suspend operator fun invoke(writer: BufferedWriter, myNodeNum: Int, filterPortnum: Int? = null) { + suspend operator fun invoke(sink: BufferedSink, myNodeNum: Int, filterPortnum: Int? = null) { val nodes = nodeRepository.nodeDBbyNum.value val positionToPos: (ProtoPosition?) -> Position? = { meshPosition -> meshPosition?.let { Position(it) }?.takeIf { it.isValid() } @@ -53,11 +54,10 @@ constructor( val nodePositions = mutableMapOf() @Suppress("MaxLineLength") - writer.appendLine( - "\"date\",\"time\",\"from\",\"sender name\",\"sender lat\",\"sender long\",\"rx lat\",\"rx long\",\"rx elevation\",\"rx snr\",\"distance(m)\",\"hop limit\",\"payload\"", + sink.writeUtf8( + "\"date\",\"time\",\"from\",\"sender name\",\"sender lat\",\"sender long\",\"rx lat\",\"rx long\",\"rx elevation\",\"rx snr\",\"distance(m)\",\"hop limit\",\"payload\"\n", ) - val dateFormat = SimpleDateFormat("\"yyyy-MM-dd\",\"HH:mm:ss\"", Locale.getDefault()) meshLogRepository.getAllLogsInReceiveOrder(Int.MAX_VALUE).first().forEach { packet -> packet.nodeInfo?.let { nodeInfo -> positionToPos.invoke(nodeInfo.position)?.let { nodePositions[nodeInfo.num] = nodeInfo.position } @@ -74,7 +74,10 @@ constructor( (filterPortnum == null || (proto.decoded?.portnum?.value ?: 0) == filterPortnum) && proto.rx_snr != 0.0f ) { - val rxDateTime = dateFormat.format(packet.received_date) + val timeZone = TimeZone.currentSystemDefault() + val rxDateTimeObj = Instant.fromEpochMilliseconds(packet.received_date).toLocalDateTime(timeZone) + val timeString = rxDateTimeObj.time.toString().substringBefore('.') + val rxDateTime = "\"${rxDateTimeObj.date}\",\"$timeString\"" val rxFrom = proto.from.toUInt() val senderName = nodes[proto.from]?.user?.long_name ?: "" @@ -112,11 +115,12 @@ constructor( } @Suppress("MaxLineLength") - writer.appendLine( - "$rxDateTime,\"$rxFrom\",\"$senderName\",\"$senderLat\",\"$senderLong\",\"$rxLat\",\"$rxLong\",\"$rxAlt\",\"$rxSnr\",\"$dist\",\"$hopLimit\",\"$payload\"", + sink.writeUtf8( + "$rxDateTime,\"$rxFrom\",\"$senderName\",\"$senderLat\",\"$senderLong\",\"$rxLat\",\"$rxLong\",\"$rxAlt\",\"$rxSnr\",\"$dist\",\"$hopLimit\",\"$payload\"\n", ) } } } + sink.flush() } } diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCase.kt similarity index 77% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCase.kt index 50d82d744..e9e8995bb 100644 --- a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCase.kt +++ b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCase.kt @@ -16,20 +16,21 @@ */ package org.meshtastic.core.domain.usecase.settings +import okio.BufferedSink import org.meshtastic.proto.DeviceProfile -import java.io.OutputStream import javax.inject.Inject /** Use case for exporting a device profile to an output stream. */ open class ExportProfileUseCase @Inject constructor() { /** - * Exports the provided [DeviceProfile] to the given [OutputStream]. + * Exports the provided [DeviceProfile] to the given [BufferedSink]. * - * @param outputStream The stream to write the profile to. + * @param sink The sink to write the profile to. * @param profile The device profile to export. * @return A [Result] indicating success or failure. */ - operator fun invoke(outputStream: OutputStream, profile: DeviceProfile): Result = runCatching { - outputStream.write(profile.encode()) + operator fun invoke(sink: BufferedSink, profile: DeviceProfile): Result = runCatching { + sink.write(profile.encode()) + sink.flush() } } diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCase.kt similarity index 53% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCase.kt index a48cc6477..55cc5032f 100644 --- a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCase.kt +++ b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCase.kt @@ -16,43 +16,36 @@ */ package org.meshtastic.core.domain.usecase.settings -import android.util.Base64 -import org.json.JSONObject +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put +import okio.BufferedSink import org.meshtastic.core.common.util.nowMillis import org.meshtastic.proto.Config -import java.io.OutputStream import javax.inject.Inject /** Use case for exporting security configuration to a JSON format. */ open class ExportSecurityConfigUseCase @Inject constructor() { /** - * Exports the provided [Config.SecurityConfig] as a JSON string to the given [OutputStream]. + * Exports the provided [Config.SecurityConfig] as a JSON string to the given [BufferedSink]. * - * @param outputStream The stream to write the JSON to. + * @param sink The sink to write the JSON to. * @param securityConfig The security configuration to export. * @return A [Result] indicating success or failure. */ - operator fun invoke(outputStream: OutputStream, securityConfig: Config.SecurityConfig): Result = runCatching { - val publicKeyBytes = securityConfig.public_key.toByteArray() - val privateKeyBytes = securityConfig.private_key.toByteArray() - - // Convert byte arrays to Base64 strings - val publicKeyBase64 = Base64.encodeToString(publicKeyBytes, Base64.NO_WRAP) - val privateKeyBase64 = Base64.encodeToString(privateKeyBytes, Base64.NO_WRAP) + operator fun invoke(sink: BufferedSink, securityConfig: Config.SecurityConfig): Result = runCatching { + // Convert ByteStrings to Base64 strings + val publicKeyBase64 = securityConfig.public_key.base64() + val privateKeyBase64 = securityConfig.private_key.base64() // Create a JSON object - val jsonObject = - JSONObject().apply { - put("timestamp", nowMillis) - put("public_key", publicKeyBase64) - put("private_key", privateKeyBase64) - } + val jsonObject = buildJsonObject { + put("timestamp", nowMillis) + put("public_key", publicKeyBase64) + put("private_key", privateKeyBase64) + } - val jsonString = jsonObject.toString(JSON_INDENT_SPACES) - outputStream.write(jsonString.toByteArray(Charsets.UTF_8)) - } - - private companion object { - private const val JSON_INDENT_SPACES = 4 + val jsonString = jsonObject.toString() + sink.writeUtf8(jsonString) + sink.flush() } } diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt similarity index 79% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt index d78d71693..c003b82ef 100644 --- a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt +++ b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt @@ -16,20 +16,20 @@ */ package org.meshtastic.core.domain.usecase.settings +import okio.BufferedSource import org.meshtastic.proto.DeviceProfile -import java.io.InputStream import javax.inject.Inject /** Use case for importing a device profile from an input stream. */ open class ImportProfileUseCase @Inject constructor() { /** - * Imports a [DeviceProfile] from the provided [InputStream]. + * Imports a [DeviceProfile] from the provided [BufferedSource]. * - * @param inputStream The stream to read the profile from. + * @param source The source to read the profile from. * @return A [Result] containing the imported [DeviceProfile] or an error. */ - operator fun invoke(inputStream: InputStream): Result = runCatching { - val bytes = inputStream.readBytes() + operator fun invoke(source: BufferedSource): Result = runCatching { + val bytes = source.readByteArray() DeviceProfile.ADAPTER.decode(bytes) } } diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCase.kt diff --git a/core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCase.kt b/core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCase.kt similarity index 100% rename from core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCase.kt rename to core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCase.kt diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/FakeRadioController.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/FakeRadioController.kt similarity index 100% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/FakeRadioController.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/FakeRadioController.kt diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/SendMessageUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/SendMessageUseCaseTest.kt similarity index 97% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/SendMessageUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/SendMessageUseCaseTest.kt index c10045b88..154df7a96 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/SendMessageUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/SendMessageUseCaseTest.kt @@ -24,11 +24,6 @@ import io.mockk.slot import io.mockk.unmockkAll import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test import org.meshtastic.core.domain.FakeRadioController import org.meshtastic.core.model.Capabilities import org.meshtastic.core.model.DataPacket @@ -40,6 +35,11 @@ import org.meshtastic.core.repository.PacketRepository import org.meshtastic.core.repository.usecase.SendMessageUseCase import org.meshtastic.proto.Config import org.meshtastic.proto.DeviceMetadata +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue class SendMessageUseCaseTest { @@ -50,7 +50,7 @@ class SendMessageUseCaseTest { private lateinit var messageQueue: MessageQueue private lateinit var useCase: SendMessageUseCase - @Before + @BeforeTest fun setUp() { nodeRepository = mockk(relaxed = true) packetRepository = mockk(relaxed = true) @@ -70,7 +70,7 @@ class SendMessageUseCaseTest { mockkConstructor(Capabilities::class) } - @After + @AfterTest fun tearDown() { unmockkAll() } diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCaseTest.kt similarity index 96% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCaseTest.kt index a6fe77b73..7fcb1cb8b 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/AdminActionsUseCaseTest.kt @@ -20,11 +20,11 @@ import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Test import org.meshtastic.core.model.RadioController import org.meshtastic.core.repository.NodeRepository +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals class AdminActionsUseCaseTest { @@ -32,7 +32,7 @@ class AdminActionsUseCaseTest { private lateinit var nodeRepository: NodeRepository private lateinit var useCase: AdminActionsUseCase - @Before + @BeforeTest fun setUp() { radioController = mockk(relaxed = true) nodeRepository = mockk(relaxed = true) diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCaseTest.kt similarity index 96% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCaseTest.kt index e8631beb2..90dbe9aa6 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/CleanNodeDatabaseUseCaseTest.kt @@ -20,12 +20,12 @@ import io.mockk.coEvery import io.mockk.coVerify import io.mockk.mockk import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Test import org.meshtastic.core.domain.FakeRadioController import org.meshtastic.core.model.Node import org.meshtastic.core.repository.NodeRepository +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals import kotlin.time.Duration.Companion.days class CleanNodeDatabaseUseCaseTest { @@ -34,7 +34,7 @@ class CleanNodeDatabaseUseCaseTest { private lateinit var radioController: FakeRadioController private lateinit var useCase: CleanNodeDatabaseUseCase - @Before + @BeforeTest fun setUp() { nodeRepository = mockk(relaxed = true) radioController = FakeRadioController() diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCaseTest.kt similarity index 79% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCaseTest.kt index f97ffe525..861cbf140 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCaseTest.kt @@ -21,11 +21,8 @@ import io.mockk.mockk import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest +import okio.Buffer import okio.ByteString.Companion.encodeUtf8 -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith import org.meshtastic.core.database.entity.MeshLog import org.meshtastic.core.model.Node import org.meshtastic.core.repository.MeshLogRepository @@ -35,18 +32,17 @@ import org.meshtastic.proto.FromRadio import org.meshtastic.proto.MeshPacket import org.meshtastic.proto.PortNum import org.meshtastic.proto.User -import org.robolectric.RobolectricTestRunner -import java.io.BufferedWriter -import java.io.StringWriter +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertTrue -@RunWith(RobolectricTestRunner::class) class ExportDataUseCaseTest { private lateinit var nodeRepository: NodeRepository private lateinit var meshLogRepository: MeshLogRepository private lateinit var useCase: ExportDataUseCase - @Before + @BeforeTest fun setUp() { nodeRepository = mockk(relaxed = true) meshLogRepository = mockk(relaxed = true) @@ -82,17 +78,15 @@ class ExportDataUseCaseTest { ) every { meshLogRepository.getAllLogsInReceiveOrder(any()) } returns flowOf(listOf(meshLog)) - val stringWriter = StringWriter() - val bufferedWriter = BufferedWriter(stringWriter) + val buffer = Buffer() // Act - useCase(bufferedWriter, myNodeNum) - bufferedWriter.flush() + useCase(buffer, myNodeNum) // Assert - val output = stringWriter.toString() - assertTrue("Header should be present", output.contains("\"date\",\"time\",\"from\",\"sender name\"")) - assertTrue("Sender name should be present", output.contains("Sender Name")) - assertTrue("Payload should be present", output.contains("Hello")) + val output = buffer.readUtf8() + assertTrue(output.contains("\"date\",\"time\",\"from\",\"sender name\""), "Header should be present") + assertTrue(output.contains("Sender Name"), "Sender name should be present") + assertTrue(output.contains("Hello"), "Payload should be present") } } diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCaseTest.kt similarity index 77% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCaseTest.kt index e2e26f4f2..99efacd64 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ExportProfileUseCaseTest.kt @@ -16,18 +16,18 @@ */ package org.meshtastic.core.domain.usecase.settings -import org.junit.Assert.assertArrayEquals -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test +import okio.Buffer import org.meshtastic.proto.DeviceProfile -import java.io.ByteArrayOutputStream +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertTrue class ExportProfileUseCaseTest { private lateinit var useCase: ExportProfileUseCase - @Before + @BeforeTest fun setUp() { useCase = ExportProfileUseCase() } @@ -36,13 +36,13 @@ class ExportProfileUseCaseTest { fun `invoke writes encoded profile to output stream`() { // Arrange val profile = DeviceProfile(long_name = "Export Node") - val outputStream = ByteArrayOutputStream() + val buffer = Buffer() // Act - val result = useCase(outputStream, profile) + val result = useCase(buffer, profile) // Assert assertTrue(result.isSuccess) - assertArrayEquals(profile.encode(), outputStream.toByteArray()) + assertContentEquals(profile.encode(), buffer.readByteArray()) } } diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCaseTest.kt similarity index 66% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCaseTest.kt index b86569cd0..a7dec65d2 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ExportSecurityConfigUseCaseTest.kt @@ -16,23 +16,22 @@ */ package org.meshtastic.core.domain.usecase.settings +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import okio.Buffer import okio.ByteString.Companion.toByteString -import org.json.JSONObject -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith import org.meshtastic.proto.Config -import org.robolectric.RobolectricTestRunner -import java.io.ByteArrayOutputStream +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue -@RunWith(RobolectricTestRunner::class) class ExportSecurityConfigUseCaseTest { private lateinit var useCase: ExportSecurityConfigUseCase - @Before + @BeforeTest fun setUp() { useCase = ExportSecurityConfigUseCase() } @@ -43,19 +42,19 @@ class ExportSecurityConfigUseCaseTest { val publicKey = byteArrayOf(1, 2, 3).toByteString() val privateKey = byteArrayOf(4, 5, 6).toByteString() val config = Config.SecurityConfig(public_key = publicKey, private_key = privateKey) - val outputStream = ByteArrayOutputStream() + val buffer = Buffer() // Act - val result = useCase(outputStream, config) + val result = useCase(buffer, config) // Assert assertTrue(result.isSuccess) - val json = JSONObject(outputStream.toString()) - assertTrue(json.has("timestamp")) - assertTrue(json.has("public_key")) - assertTrue(json.has("private_key")) + val json = Json.parseToJsonElement(buffer.readUtf8()).jsonObject + assertTrue(json.containsKey("timestamp")) + assertTrue(json.containsKey("public_key")) + assertTrue(json.containsKey("private_key")) // Check base64 values - assertEquals("AQID", json.getString("public_key")) - assertEquals("BAUG", json.getString("private_key")) + assertEquals("AQID", json["public_key"]?.jsonPrimitive?.content) + assertEquals("BAUG", json["private_key"]?.jsonPrimitive?.content) } } diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCaseTest.kt similarity index 78% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCaseTest.kt index 7b41a67f8..e0343b75a 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCaseTest.kt @@ -16,18 +16,18 @@ */ package org.meshtastic.core.domain.usecase.settings -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test +import okio.Buffer import org.meshtastic.proto.DeviceProfile -import java.io.ByteArrayInputStream +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue class ImportProfileUseCaseTest { private lateinit var useCase: ImportProfileUseCase - @Before + @BeforeTest fun setUp() { useCase = ImportProfileUseCase() } @@ -36,10 +36,10 @@ class ImportProfileUseCaseTest { fun `invoke with valid data returns profile`() { // Arrange val profile = DeviceProfile(long_name = "Test Node") - val inputStream = ByteArrayInputStream(profile.encode()) + val buffer = Buffer().write(profile.encode()) // Act - val result = useCase(inputStream) + val result = useCase(buffer) // Assert assertTrue(result.isSuccess) @@ -49,10 +49,10 @@ class ImportProfileUseCaseTest { @Test fun `invoke with invalid data returns failure`() { // Arrange - val inputStream = ByteArrayInputStream(byteArrayOf(1, 2, 3)) + val buffer = Buffer().write(byteArrayOf(1, 2, 3)) // Act - val result = useCase(inputStream) + val result = useCase(buffer) // Assert assertTrue(result.isFailure) diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCaseTest.kt similarity index 97% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCaseTest.kt index 411d47a92..08f011bcb 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/InstallProfileUseCaseTest.kt @@ -20,8 +20,6 @@ import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test import org.meshtastic.core.model.RadioController import org.meshtastic.proto.Config import org.meshtastic.proto.DeviceProfile @@ -29,13 +27,15 @@ import org.meshtastic.proto.LocalConfig import org.meshtastic.proto.LocalModuleConfig import org.meshtastic.proto.ModuleConfig import org.meshtastic.proto.User +import kotlin.test.BeforeTest +import kotlin.test.Test class InstallProfileUseCaseTest { private lateinit var radioController: RadioController private lateinit var useCase: InstallProfileUseCase - @Before + @BeforeTest fun setUp() { radioController = mockk(relaxed = true) useCase = InstallProfileUseCase(radioController) diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCaseTest.kt similarity index 97% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCaseTest.kt index dc17b7cd2..30573f11b 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCaseTest.kt @@ -22,16 +22,16 @@ import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test import org.meshtastic.core.model.ConnectionState import org.meshtastic.core.model.Node import org.meshtastic.core.model.RadioController import org.meshtastic.core.repository.DeviceHardwareRepository import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.RadioPrefs +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue class IsOtaCapableUseCaseTest { @@ -44,7 +44,7 @@ class IsOtaCapableUseCaseTest { private val ourNodeInfoFlow = MutableStateFlow(null) private val connectionStateFlow = MutableStateFlow(ConnectionState.Disconnected) - @Before + @BeforeTest fun setUp() { nodeRepository = mockk { every { ourNodeInfo } returns ourNodeInfoFlow } radioController = mockk { every { connectionState } returns connectionStateFlow } diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCaseTest.kt similarity index 95% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCaseTest.kt index 95910cc78..44de5cd95 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/MeshLocationUseCaseTest.kt @@ -18,16 +18,16 @@ package org.meshtastic.core.domain.usecase.settings import io.mockk.mockk import io.mockk.verify -import org.junit.Before -import org.junit.Test import org.meshtastic.core.model.RadioController +import kotlin.test.BeforeTest +import kotlin.test.Test class MeshLocationUseCaseTest { private lateinit var radioController: RadioController private lateinit var useCase: MeshLocationUseCase - @Before + @BeforeTest fun setUp() { radioController = mockk(relaxed = true) useCase = MeshLocationUseCase(radioController) diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt similarity index 96% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt index 9489a804e..550d76fbb 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ProcessRadioResponseUseCaseTest.kt @@ -16,22 +16,22 @@ */ package org.meshtastic.core.domain.usecase.settings -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test import org.meshtastic.proto.AdminMessage import org.meshtastic.proto.Data import org.meshtastic.proto.DeviceMetadata import org.meshtastic.proto.MeshPacket import org.meshtastic.proto.PortNum import org.meshtastic.proto.Routing +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue class ProcessRadioResponseUseCaseTest { private lateinit var useCase: ProcessRadioResponseUseCase - @Before + @BeforeTest fun setUp() { useCase = ProcessRadioResponseUseCase() } diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCaseTest.kt similarity index 98% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCaseTest.kt index 29e26406c..8f42672ff 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/RadioConfigUseCaseTest.kt @@ -20,22 +20,22 @@ import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Test import org.meshtastic.core.model.Position import org.meshtastic.core.model.RadioController import org.meshtastic.proto.Channel import org.meshtastic.proto.Config import org.meshtastic.proto.ModuleConfig import org.meshtastic.proto.User +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals class RadioConfigUseCaseTest { private lateinit var radioController: RadioController private lateinit var useCase: RadioConfigUseCase - @Before + @BeforeTest fun setUp() { radioController = mockk(relaxed = true) useCase = RadioConfigUseCase(radioController) diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCaseTest.kt similarity index 95% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCaseTest.kt index 08e485c9a..c9268e8a7 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetAppIntroCompletedUseCaseTest.kt @@ -18,16 +18,16 @@ package org.meshtastic.core.domain.usecase.settings import io.mockk.mockk import io.mockk.verify -import org.junit.Before -import org.junit.Test import org.meshtastic.core.datastore.UiPreferencesDataSource +import kotlin.test.BeforeTest +import kotlin.test.Test class SetAppIntroCompletedUseCaseTest { private lateinit var uiPreferencesDataSource: UiPreferencesDataSource private lateinit var useCase: SetAppIntroCompletedUseCase - @Before + @BeforeTest fun setUp() { uiPreferencesDataSource = mockk(relaxed = true) useCase = SetAppIntroCompletedUseCase(uiPreferencesDataSource) diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCaseTest.kt similarity index 95% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCaseTest.kt index 8a31155ad..95e134517 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCaseTest.kt @@ -18,17 +18,17 @@ package org.meshtastic.core.domain.usecase.settings import io.mockk.mockk import io.mockk.verify -import org.junit.Before -import org.junit.Test import org.meshtastic.core.common.database.DatabaseManager import org.meshtastic.core.database.DatabaseConstants +import kotlin.test.BeforeTest +import kotlin.test.Test class SetDatabaseCacheLimitUseCaseTest { private lateinit var databaseManager: DatabaseManager private lateinit var useCase: SetDatabaseCacheLimitUseCase - @Before + @BeforeTest fun setUp() { databaseManager = mockk(relaxed = true) useCase = SetDatabaseCacheLimitUseCase(databaseManager) diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCaseTest.kt similarity index 97% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCaseTest.kt index cac857b69..a7aaf8fb2 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCaseTest.kt @@ -21,10 +21,10 @@ import io.mockk.every import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test import org.meshtastic.core.repository.MeshLogPrefs import org.meshtastic.core.repository.MeshLogRepository +import kotlin.test.BeforeTest +import kotlin.test.Test class SetMeshLogSettingsUseCaseTest { @@ -32,7 +32,7 @@ class SetMeshLogSettingsUseCaseTest { private lateinit var meshLogPrefs: MeshLogPrefs private lateinit var useCase: SetMeshLogSettingsUseCase - @Before + @BeforeTest fun setUp() { meshLogRepository = mockk(relaxed = true) meshLogPrefs = mockk(relaxed = true) diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCaseTest.kt similarity index 94% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCaseTest.kt index 5877cbf1e..cdd1108c8 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCaseTest.kt @@ -18,16 +18,16 @@ package org.meshtastic.core.domain.usecase.settings import io.mockk.mockk import io.mockk.verify -import org.junit.Before -import org.junit.Test import org.meshtastic.core.repository.UiPrefs +import kotlin.test.BeforeTest +import kotlin.test.Test class SetProvideLocationUseCaseTest { private lateinit var uiPrefs: UiPrefs private lateinit var useCase: SetProvideLocationUseCase - @Before + @BeforeTest fun setUp() { uiPrefs = mockk(relaxed = true) useCase = SetProvideLocationUseCase(uiPrefs) diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCaseTest.kt similarity index 95% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCaseTest.kt index 7d04ce7bc..4a49bf451 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetThemeUseCaseTest.kt @@ -18,16 +18,16 @@ package org.meshtastic.core.domain.usecase.settings import io.mockk.mockk import io.mockk.verify -import org.junit.Before -import org.junit.Test import org.meshtastic.core.datastore.UiPreferencesDataSource +import kotlin.test.BeforeTest +import kotlin.test.Test class SetThemeUseCaseTest { private lateinit var uiPreferencesDataSource: UiPreferencesDataSource private lateinit var useCase: SetThemeUseCase - @Before + @BeforeTest fun setUp() { uiPreferencesDataSource = mockk(relaxed = true) useCase = SetThemeUseCase(uiPreferencesDataSource) diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCaseTest.kt similarity index 96% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCaseTest.kt index 3dea1fd20..fd1de9a74 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCaseTest.kt @@ -19,16 +19,16 @@ package org.meshtastic.core.domain.usecase.settings import io.mockk.every import io.mockk.mockk import io.mockk.verify -import org.junit.Before -import org.junit.Test import org.meshtastic.core.repository.AnalyticsPrefs +import kotlin.test.BeforeTest +import kotlin.test.Test class ToggleAnalyticsUseCaseTest { private lateinit var analyticsPrefs: AnalyticsPrefs private lateinit var useCase: ToggleAnalyticsUseCase - @Before + @BeforeTest fun setUp() { analyticsPrefs = mockk(relaxed = true) useCase = ToggleAnalyticsUseCase(analyticsPrefs) diff --git a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCaseTest.kt similarity index 96% rename from core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCaseTest.kt rename to core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCaseTest.kt index 9789ad703..fc30c1548 100644 --- a/core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCaseTest.kt +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCaseTest.kt @@ -19,16 +19,16 @@ package org.meshtastic.core.domain.usecase.settings import io.mockk.every import io.mockk.mockk import io.mockk.verify -import org.junit.Before -import org.junit.Test import org.meshtastic.core.repository.HomoglyphPrefs +import kotlin.test.BeforeTest +import kotlin.test.Test class ToggleHomoglyphEncodingUseCaseTest { private lateinit var homoglyphEncodingPrefs: HomoglyphPrefs private lateinit var useCase: ToggleHomoglyphEncodingUseCase - @Before + @BeforeTest fun setUp() { homoglyphEncodingPrefs = mockk(relaxed = true) useCase = ToggleHomoglyphEncodingUseCase(homoglyphEncodingPrefs) diff --git a/core/model/detekt-baseline.xml b/core/model/detekt-baseline.xml index 99ebbdc7e..027b5adc5 100644 --- a/core/model/detekt-baseline.xml +++ b/core/model/detekt-baseline.xml @@ -2,12 +2,7 @@ - MagicNumber:ChannelSet.kt$40 - MagicNumber:ChannelSet.kt$960 - SwallowedException:ChannelSet.kt$ex: Throwable SwallowedException:DataPacket.kt$DataPacket$e: Exception - TooGenericExceptionCaught:ChannelSet.kt$ex: Throwable TooGenericExceptionCaught:DataPacket.kt$DataPacket$e: Exception - UnusedPrivateMember:DataPacket.kt$private inline fun <reified T : Parcelable> Parcel.readParcelableCompat(loader: ClassLoader?): T? diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index badef0833..7085433ce 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -14,36 +14,52 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import com.android.build.api.dsl.LibraryExtension plugins { - alias(libs.plugins.meshtastic.android.library) - alias(libs.plugins.meshtastic.android.library.flavors) - alias(libs.plugins.meshtastic.hilt) - alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.meshtastic.kmp.library) + alias(libs.plugins.meshtastic.kotlinx.serialization) + alias(libs.plugins.devtools.ksp) } -configure { - buildFeatures { buildConfig = true } - namespace = "org.meshtastic.core.network" +kotlin { + @Suppress("UnstableApiUsage") + android { + namespace = "org.meshtastic.core.network" + androidResources.enable = false + } + + sourceSets { + commonMain.dependencies { + api(projects.core.repository) + implementation(projects.core.di) + implementation(projects.core.model) + implementation(projects.core.proto) + + api(libs.javax.inject) + implementation(libs.okio) + implementation(libs.kotlinx.serialization.json) + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.content.negotiation) + implementation(libs.ktor.serialization.kotlinx.json) + implementation(libs.kermit) + } + androidMain.dependencies { + implementation(libs.hilt.android) + implementation(libs.org.eclipse.paho.client.mqttv3) + implementation(libs.coil.network.okhttp) + implementation(libs.coil.svg) + implementation(libs.ktor.client.okhttp) + implementation(libs.okhttp3.logging.interceptor) + } + } } -dependencies { - api(projects.core.repository) - implementation(projects.core.di) - implementation(projects.core.model) - implementation(projects.core.proto) +val marketplaceAttr = Attribute.of("marketplace", String::class.java) - implementation(libs.org.eclipse.paho.client.mqttv3) - implementation(libs.okio) - implementation(libs.coil.network.okhttp) - implementation(libs.coil.svg) - implementation(libs.kotlinx.serialization.json) - implementation(libs.ktor.client.content.negotiation) - implementation(libs.ktor.client.okhttp) - implementation(libs.ktor.serialization.kotlinx.json) - implementation(libs.okhttp3.logging.interceptor) - implementation(libs.kermit) - - googleImplementation(libs.dd.sdk.android.okhttp) +configurations.all { + if (name.contains("android", ignoreCase = true)) { + attributes.attribute(marketplaceAttr, "fdroid") + } } + +dependencies { add("kspAndroid", libs.hilt.compiler) } diff --git a/core/network/src/main/kotlin/org/meshtastic/core/network/repository/MQTTRepository.kt b/core/network/src/androidMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt similarity index 96% rename from core/network/src/main/kotlin/org/meshtastic/core/network/repository/MQTTRepository.kt rename to core/network/src/androidMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt index 960f4d843..86590e6cb 100644 --- a/core/network/src/main/kotlin/org/meshtastic/core/network/repository/MQTTRepository.kt +++ b/core/network/src/androidMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt @@ -43,12 +43,12 @@ import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager @Singleton -class MQTTRepository +class MQTTRepositoryImpl @Inject constructor( private val radioConfigRepository: RadioConfigRepository, private val nodeRepository: NodeRepository, -) { +) : MQTTRepository { companion object { /** @@ -67,7 +67,7 @@ constructor( private var mqttClient: MqttAsyncClient? = null - fun disconnect() { + override fun disconnect() { Logger.i { "MQTT Disconnected" } mqttClient?.apply { if (isConnected) { @@ -78,7 +78,7 @@ constructor( mqttClient = null } - val proxyMessageFlow: Flow = callbackFlow { + override val proxyMessageFlow: Flow = callbackFlow { val ownerId = "MeshtasticAndroidMqttProxy-${nodeRepository.myId.value ?: generateClientId()}" val channelSet = radioConfigRepository.channelSetFlow.first() val mqttConfig = radioConfigRepository.moduleConfigFlow.first().mqtt @@ -165,7 +165,7 @@ constructor( } @Suppress("TooGenericExceptionCaught") - fun publish(topic: String, data: ByteArray, retained: Boolean) { + override fun publish(topic: String, data: ByteArray, retained: Boolean) { try { val token = mqttClient?.publish(topic, data, DEFAULT_QOS, retained) Logger.i { "MQTT Publish messageId: ${token?.messageId}" } diff --git a/core/network/src/main/kotlin/org/meshtastic/core/network/repository/TrustAllX509TrustManager.kt b/core/network/src/androidMain/kotlin/org/meshtastic/core/network/repository/TrustAllX509TrustManager.kt similarity index 100% rename from core/network/src/main/kotlin/org/meshtastic/core/network/repository/TrustAllX509TrustManager.kt rename to core/network/src/androidMain/kotlin/org/meshtastic/core/network/repository/TrustAllX509TrustManager.kt diff --git a/core/network/src/main/kotlin/org/meshtastic/core/network/DeviceHardwareRemoteDataSource.kt b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/DeviceHardwareRemoteDataSource.kt similarity index 100% rename from core/network/src/main/kotlin/org/meshtastic/core/network/DeviceHardwareRemoteDataSource.kt rename to core/network/src/commonMain/kotlin/org/meshtastic/core/network/DeviceHardwareRemoteDataSource.kt diff --git a/core/network/src/main/kotlin/org/meshtastic/core/network/FirmwareReleaseRemoteDataSource.kt b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/FirmwareReleaseRemoteDataSource.kt similarity index 100% rename from core/network/src/main/kotlin/org/meshtastic/core/network/FirmwareReleaseRemoteDataSource.kt rename to core/network/src/commonMain/kotlin/org/meshtastic/core/network/FirmwareReleaseRemoteDataSource.kt diff --git a/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepository.kt b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepository.kt new file mode 100644 index 000000000..fe092fd7c --- /dev/null +++ b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepository.kt @@ -0,0 +1,41 @@ +/* + * 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 . + */ +package org.meshtastic.core.network.repository + +import kotlinx.coroutines.flow.Flow +import org.meshtastic.proto.MqttClientProxyMessage + +/** Interface defining the MQTT interactions used for proxying messages to and from the mesh. */ +interface MQTTRepository { + /** Disconnects the MQTT client and cleans up resources. */ + fun disconnect() + + /** + * A flow of incoming messages from the subscribed MQTT topics. Connecting/subscribing is initiated when this flow + * is collected. + */ + val proxyMessageFlow: Flow + + /** + * Publishes a message to the given MQTT topic. + * + * @param topic The MQTT topic to publish to. + * @param data The binary payload. + * @param retained Whether the message should be retained by the broker. + */ + fun publish(topic: String, data: ByteArray, retained: Boolean) +} diff --git a/core/network/src/main/kotlin/org/meshtastic/core/network/service/ApiService.kt b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/service/ApiService.kt similarity index 97% rename from core/network/src/main/kotlin/org/meshtastic/core/network/service/ApiService.kt rename to core/network/src/commonMain/kotlin/org/meshtastic/core/network/service/ApiService.kt index 755a88568..a8a813614 100644 --- a/core/network/src/main/kotlin/org/meshtastic/core/network/service/ApiService.kt +++ b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/service/ApiService.kt @@ -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,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.network.service import io.ktor.client.HttpClient diff --git a/core/network/src/main/AndroidManifest.xml b/core/network/src/main/AndroidManifest.xml deleted file mode 100644 index a8800291f..000000000 --- a/core/network/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/core/network/src/main/kotlin/org/meshtastic/core/network/di/NetworkModule.kt b/core/network/src/main/kotlin/org/meshtastic/core/network/di/NetworkModule.kt deleted file mode 100644 index 354487614..000000000 --- a/core/network/src/main/kotlin/org/meshtastic/core/network/di/NetworkModule.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2025-2026 Meshtastic LLC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.meshtastic.core.network.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.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.network.BuildConfig -import javax.inject.Singleton - -private const val DISK_CACHE_PERCENT = 0.02 -private const val MEMORY_CACHE_PERCENT = 0.25 - -@InstallIn(SingletonComponent::class) -@Module -class NetworkModule { - - @Provides - @Singleton - fun provideImageLoader(okHttpClient: OkHttpClient, @ApplicationContext application: Context): 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 (BuildConfig.DEBUG) DebugLogger(minLevel = Logger.Level.Verbose) else null) - .crossfade(enable = true) - .build() - } - - @Provides - @Singleton - fun provideHttpClient(okHttpClient: OkHttpClient): HttpClient = HttpClient(engineFactory = OkHttp) { - engine { preconfigured = okHttpClient } - - install(plugin = ContentNegotiation) { - json( - Json { - isLenient = true - ignoreUnknownKeys = true - }, - ) - } - } -} diff --git a/core/prefs/build.gradle.kts b/core/prefs/build.gradle.kts index 844495e6b..f2d34d56e 100644 --- a/core/prefs/build.gradle.kts +++ b/core/prefs/build.gradle.kts @@ -14,25 +14,37 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import com.android.build.api.dsl.LibraryExtension plugins { - alias(libs.plugins.meshtastic.android.library) - alias(libs.plugins.meshtastic.android.library.flavors) - alias(libs.plugins.meshtastic.hilt) + alias(libs.plugins.meshtastic.kmp.library) + alias(libs.plugins.devtools.ksp) } -configure { namespace = "org.meshtastic.core.prefs" } +kotlin { + @Suppress("UnstableApiUsage") + android { + namespace = "org.meshtastic.core.prefs" + androidResources.enable = false + withHostTest { isIncludeAndroidResources = true } + } -dependencies { - implementation(projects.core.repository) - implementation(projects.core.common) - implementation(projects.core.di) - implementation(libs.androidx.datastore.preferences) - implementation(libs.kotlinx.coroutines.core) - googleImplementation(libs.maps.compose) + sourceSets { + commonMain.dependencies { + implementation(projects.core.repository) + implementation(projects.core.common) + implementation(projects.core.di) - testImplementation(libs.junit) - testImplementation(libs.mockk) - testImplementation(libs.kotlinx.coroutines.test) + api(libs.javax.inject) + implementation(libs.androidx.datastore.preferences) + implementation(libs.kotlinx.coroutines.core) + } + + commonTest.dependencies { + implementation(kotlin("test")) + implementation(libs.kotlinx.coroutines.test) + implementation(libs.mockk) + } + } } + +dependencies { add("kspAndroid", libs.hilt.compiler) } diff --git a/core/prefs/src/test/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsTest.kt b/core/prefs/src/androidUnitTest/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsTest.kt similarity index 100% rename from core/prefs/src/test/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsTest.kt rename to core/prefs/src/androidUnitTest/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsTest.kt diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/analytics/AnalyticsPrefsImpl.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/analytics/AnalyticsPrefsImpl.kt similarity index 100% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/analytics/AnalyticsPrefsImpl.kt rename to core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/analytics/AnalyticsPrefsImpl.kt diff --git a/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/di/Qualifiers.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/di/Qualifiers.kt new file mode 100644 index 000000000..453ec6bc6 --- /dev/null +++ b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/di/Qualifiers.kt @@ -0,0 +1,67 @@ +/* + * 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 . + */ +package org.meshtastic.core.prefs.di + +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class AnalyticsDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class HomoglyphEncodingDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class AppDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class CustomEmojiDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class MapDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class MapConsentDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class MapTileProviderDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class MeshDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class RadioDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class UiDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class MeshLogDataStore + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class FilterDataStore diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/emoji/CustomEmojiPrefsImpl.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/emoji/CustomEmojiPrefsImpl.kt similarity index 100% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/emoji/CustomEmojiPrefsImpl.kt rename to core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/emoji/CustomEmojiPrefsImpl.kt diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsImpl.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsImpl.kt similarity index 100% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsImpl.kt rename to core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsImpl.kt diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/homoglyph/HomoglyphPrefsImpl.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/homoglyph/HomoglyphPrefsImpl.kt similarity index 100% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/homoglyph/HomoglyphPrefsImpl.kt rename to core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/homoglyph/HomoglyphPrefsImpl.kt diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapConsentPrefsImpl.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/map/MapConsentPrefsImpl.kt similarity index 100% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapConsentPrefsImpl.kt rename to core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/map/MapConsentPrefsImpl.kt diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapPrefsImpl.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/map/MapPrefsImpl.kt similarity index 100% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapPrefsImpl.kt rename to core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/map/MapPrefsImpl.kt diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapTileProviderPrefsImpl.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/map/MapTileProviderPrefsImpl.kt similarity index 100% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapTileProviderPrefsImpl.kt rename to core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/map/MapTileProviderPrefsImpl.kt diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/mesh/MeshPrefsImpl.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/mesh/MeshPrefsImpl.kt similarity index 100% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/mesh/MeshPrefsImpl.kt rename to core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/mesh/MeshPrefsImpl.kt diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/meshlog/MeshLogPrefsImpl.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/meshlog/MeshLogPrefsImpl.kt similarity index 100% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/meshlog/MeshLogPrefsImpl.kt rename to core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/meshlog/MeshLogPrefsImpl.kt diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/radio/RadioPrefsImpl.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/radio/RadioPrefsImpl.kt similarity index 100% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/radio/RadioPrefsImpl.kt rename to core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/radio/RadioPrefsImpl.kt diff --git a/core/prefs/src/main/kotlin/org/meshtastic/core/prefs/ui/UiPrefsImpl.kt b/core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/ui/UiPrefsImpl.kt similarity index 100% rename from core/prefs/src/main/kotlin/org/meshtastic/core/prefs/ui/UiPrefsImpl.kt rename to core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/ui/UiPrefsImpl.kt diff --git a/core/repository/src/androidMain/kotlin/org/meshtastic/core/repository/Location.kt b/core/repository/src/androidMain/kotlin/org/meshtastic/core/repository/Location.kt new file mode 100644 index 000000000..54e2b1a7c --- /dev/null +++ b/core/repository/src/androidMain/kotlin/org/meshtastic/core/repository/Location.kt @@ -0,0 +1,20 @@ +/* + * 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 . + */ +package org.meshtastic.core.repository + +/** Android-specific location object typealias for KMP. */ +actual typealias Location = android.location.Location diff --git a/core/analytics/src/main/kotlin/org/meshtastic/core/analytics/DataPair.kt b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/DataPair.kt similarity index 92% rename from core/analytics/src/main/kotlin/org/meshtastic/core/analytics/DataPair.kt rename to core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/DataPair.kt index 1822c417f..e095b2dfd 100644 --- a/core/analytics/src/main/kotlin/org/meshtastic/core/analytics/DataPair.kt +++ b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/DataPair.kt @@ -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 . */ - -package org.meshtastic.core.analytics +package org.meshtastic.core.repository /** * A key-value pair for sending properties with analytics events. diff --git a/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LocationRepository.kt b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LocationRepository.kt new file mode 100644 index 000000000..2a55e9cfe --- /dev/null +++ b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LocationRepository.kt @@ -0,0 +1,31 @@ +/* + * 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 . + */ +package org.meshtastic.core.repository + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow + +/** Platform-independent location object for KMP. */ +expect class Location + +interface LocationRepository { + /** Status of whether the app is actively subscribed to location changes. */ + val receivingLocationUpdates: StateFlow + + /** Observable flow for location updates */ + fun getLocations(): Flow +} diff --git a/core/analytics/src/main/kotlin/org/meshtastic/core/analytics/platform/PlatformAnalytics.kt b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/PlatformAnalytics.kt similarity index 75% rename from core/analytics/src/main/kotlin/org/meshtastic/core/analytics/platform/PlatformAnalytics.kt rename to core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/PlatformAnalytics.kt index fe3845e92..b4ce22165 100644 --- a/core/analytics/src/main/kotlin/org/meshtastic/core/analytics/platform/PlatformAnalytics.kt +++ b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/PlatformAnalytics.kt @@ -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,12 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - -package org.meshtastic.core.analytics.platform - -import androidx.compose.runtime.Composable -import androidx.navigation.NavHostController -import org.meshtastic.core.analytics.DataPair +package org.meshtastic.core.repository /** * Interface to abstract platform-specific functionalities, primarily for analytics and related services that differ @@ -37,13 +32,6 @@ interface PlatformAnalytics { */ fun setDeviceAttributes(firmwareVersion: String, model: String) - /** - * A Composable function to set up navigation tracking for the current platform. - * - * @param navController The [NavHostController] to track. - */ - @Composable fun AddNavigationTrackingEffect(navController: NavHostController) - /** * Indicates whether platform-specific services (like Google Play Services or Datadog) are available and * initialized. diff --git a/core/service/src/main/kotlin/org/meshtastic/core/service/AndroidRadioControllerImpl.kt b/core/service/src/main/kotlin/org/meshtastic/core/service/AndroidRadioControllerImpl.kt index 052ebe321..9790eeec3 100644 --- a/core/service/src/main/kotlin/org/meshtastic/core/service/AndroidRadioControllerImpl.kt +++ b/core/service/src/main/kotlin/org/meshtastic/core/service/AndroidRadioControllerImpl.kt @@ -204,7 +204,7 @@ constructor( // Ensure service is running/restarted to handle the new address val intent = android.content.Intent().apply { - setClassName("com.geeksville.mesh", "com.geeksville.mesh.service.MeshService") + setClassName("com.geeksville.mesh", "org.meshtastic.app.service.MeshService") } context.startForegroundService(intent) } diff --git a/feature/map/build.gradle.kts b/feature/map/build.gradle.kts index c061bd993..f8b445a04 100644 --- a/feature/map/build.gradle.kts +++ b/feature/map/build.gradle.kts @@ -37,7 +37,10 @@ dependencies { implementation(projects.core.service) implementation(projects.core.resources) implementation(projects.core.ui) + implementation(projects.core.di) + implementation(libs.androidx.datastore) + implementation(libs.androidx.datastore.preferences) implementation(libs.accompanist.permissions) implementation(libs.androidx.activity.compose) implementation(libs.androidx.annotation) diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt index 8c88f99e1..d638a2f9d 100644 --- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt @@ -43,17 +43,17 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.serialization.Serializable -import org.meshtastic.core.data.model.CustomTileProviderConfig -import org.meshtastic.core.data.repository.CustomTileProviderRepository import org.meshtastic.core.datastore.UiPreferencesDataSource import org.meshtastic.core.model.RadioController import org.meshtastic.core.navigation.MapRoutes -import org.meshtastic.core.prefs.map.GoogleMapsPrefs import org.meshtastic.core.repository.MapPrefs import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.PacketRepository import org.meshtastic.core.repository.RadioConfigRepository import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed +import org.meshtastic.feature.map.model.CustomTileProviderConfig +import org.meshtastic.feature.map.prefs.map.GoogleMapsPrefs +import org.meshtastic.feature.map.repository.CustomTileProviderRepository import org.meshtastic.proto.Config import java.io.File import java.io.FileOutputStream diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomTileProviderManagerSheet.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomTileProviderManagerSheet.kt index e65f5968d..8b7e2d3aa 100644 --- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomTileProviderManagerSheet.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomTileProviderManagerSheet.kt @@ -51,7 +51,6 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.flow.collectLatest import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.data.model.CustomTileProviderConfig import org.meshtastic.core.resources.Res import org.meshtastic.core.resources.add_custom_tile_source import org.meshtastic.core.resources.add_local_mbtiles_file @@ -72,6 +71,7 @@ import org.meshtastic.core.resources.url_template_hint import org.meshtastic.core.ui.component.MeshtasticDialog import org.meshtastic.core.ui.util.showToast import org.meshtastic.feature.map.MapViewModel +import org.meshtastic.feature.map.model.CustomTileProviderConfig @Suppress("LongMethod") @Composable diff --git a/core/data/src/google/kotlin/org/meshtastic/core/data/model/CustomTileProviderConfig.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/model/CustomTileProviderConfig.kt similarity index 96% rename from core/data/src/google/kotlin/org/meshtastic/core/data/model/CustomTileProviderConfig.kt rename to feature/map/src/google/kotlin/org/meshtastic/feature/map/model/CustomTileProviderConfig.kt index 434aa834e..b188a5eb8 100644 --- a/core/data/src/google/kotlin/org/meshtastic/core/data/model/CustomTileProviderConfig.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/model/CustomTileProviderConfig.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.core.data.model +package org.meshtastic.feature.map.model import kotlinx.serialization.Serializable import kotlin.uuid.Uuid diff --git a/core/prefs/src/google/kotlin/org/meshtastic/core/prefs/di/GoogleMapsModule.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/prefs/di/GoogleMapsModule.kt similarity index 81% rename from core/prefs/src/google/kotlin/org/meshtastic/core/prefs/di/GoogleMapsModule.kt rename to feature/map/src/google/kotlin/org/meshtastic/feature/map/prefs/di/GoogleMapsModule.kt index d195087f7..c13b98ca0 100644 --- a/core/prefs/src/google/kotlin/org/meshtastic/core/prefs/di/GoogleMapsModule.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/prefs/di/GoogleMapsModule.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.core.prefs.di +package org.meshtastic.feature.map.prefs.di import android.content.Context import androidx.datastore.core.DataStore @@ -31,14 +31,16 @@ import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob -import org.meshtastic.core.prefs.map.GoogleMapsPrefs -import org.meshtastic.core.prefs.map.GoogleMapsPrefsImpl +import org.meshtastic.feature.map.prefs.map.GoogleMapsPrefs +import org.meshtastic.feature.map.prefs.map.GoogleMapsPrefsImpl +import org.meshtastic.feature.map.repository.CustomTileProviderRepository +import org.meshtastic.feature.map.repository.CustomTileProviderRepositoryImpl import javax.inject.Qualifier import javax.inject.Singleton @Qualifier @Retention(AnnotationRetention.BINARY) -internal annotation class GoogleMapsDataStore +annotation class GoogleMapsDataStore @InstallIn(SingletonComponent::class) @Module @@ -46,6 +48,10 @@ interface GoogleMapsModule { @Binds fun bindGoogleMapsPrefs(googleMapsPrefsImpl: GoogleMapsPrefsImpl): GoogleMapsPrefs + @Binds + @Singleton + fun bindCustomTileProviderRepository(impl: CustomTileProviderRepositoryImpl): CustomTileProviderRepository + companion object { private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) diff --git a/core/prefs/src/google/kotlin/org/meshtastic/core/prefs/map/GoogleMapsPrefs.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/prefs/map/GoogleMapsPrefs.kt similarity index 98% rename from core/prefs/src/google/kotlin/org/meshtastic/core/prefs/map/GoogleMapsPrefs.kt rename to feature/map/src/google/kotlin/org/meshtastic/feature/map/prefs/map/GoogleMapsPrefs.kt index a8873201d..0fb81a8f3 100644 --- a/core/prefs/src/google/kotlin/org/meshtastic/core/prefs/map/GoogleMapsPrefs.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/prefs/map/GoogleMapsPrefs.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.core.prefs.map +package org.meshtastic.feature.map.prefs.map import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences @@ -32,7 +32,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import org.meshtastic.core.di.CoroutineDispatchers -import org.meshtastic.core.prefs.di.GoogleMapsDataStore +import org.meshtastic.feature.map.prefs.di.GoogleMapsDataStore import javax.inject.Inject import javax.inject.Singleton diff --git a/core/data/src/google/kotlin/org/meshtastic/core/data/repository/CustomTileProviderRepository.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/repository/CustomTileProviderRepository.kt similarity index 97% rename from core/data/src/google/kotlin/org/meshtastic/core/data/repository/CustomTileProviderRepository.kt rename to feature/map/src/google/kotlin/org/meshtastic/feature/map/repository/CustomTileProviderRepository.kt index 5fbe32d92..1b55c2397 100644 --- a/core/data/src/google/kotlin/org/meshtastic/core/data/repository/CustomTileProviderRepository.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/repository/CustomTileProviderRepository.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.core.data.repository +package org.meshtastic.feature.map.repository import co.touchlab.kermit.Logger import kotlinx.coroutines.flow.Flow @@ -23,9 +23,9 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.withContext import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json -import org.meshtastic.core.data.model.CustomTileProviderConfig import org.meshtastic.core.di.CoroutineDispatchers import org.meshtastic.core.repository.MapTileProviderPrefs +import org.meshtastic.feature.map.model.CustomTileProviderConfig import javax.inject.Inject import javax.inject.Singleton diff --git a/feature/map/src/testGoogle/kotlin/org/meshtastic/feature/map/MapViewModelTest.kt b/feature/map/src/testGoogle/kotlin/org/meshtastic/feature/map/MapViewModelTest.kt index a66a3a255..9ec2e21f5 100644 --- a/feature/map/src/testGoogle/kotlin/org/meshtastic/feature/map/MapViewModelTest.kt +++ b/feature/map/src/testGoogle/kotlin/org/meshtastic/feature/map/MapViewModelTest.kt @@ -38,16 +38,16 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.meshtastic.core.data.model.CustomTileProviderConfig -import org.meshtastic.core.data.repository.CustomTileProviderRepository import org.meshtastic.core.datastore.UiPreferencesDataSource import org.meshtastic.core.model.ConnectionState import org.meshtastic.core.model.RadioController -import org.meshtastic.core.prefs.map.GoogleMapsPrefs import org.meshtastic.core.repository.MapPrefs import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.PacketRepository import org.meshtastic.core.repository.RadioConfigRepository +import org.meshtastic.feature.map.model.CustomTileProviderConfig +import org.meshtastic.feature.map.prefs.map.GoogleMapsPrefs +import org.meshtastic.feature.map.repository.CustomTileProviderRepository import org.robolectric.RobolectricTestRunner @OptIn(ExperimentalCoroutinesApi::class) diff --git a/feature/messaging/build.gradle.kts b/feature/messaging/build.gradle.kts index 97b81c776..81104e76f 100644 --- a/feature/messaging/build.gradle.kts +++ b/feature/messaging/build.gradle.kts @@ -25,7 +25,6 @@ plugins { configure { namespace = "org.meshtastic.feature.messaging" } dependencies { - implementation(projects.core.analytics) implementation(projects.core.common) implementation(projects.core.data) implementation(projects.core.database) diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/Message.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/Message.kt index c28a07792..4154e43df 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/Message.kt +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/Message.kt @@ -253,8 +253,9 @@ fun MessageScreen( if (hasUnreadMessages == true) { if (firstUnreadMessageUuid == null) return@LaunchedEffect // Wait for UUID query - if (firstUnreadIndex != null) { - val targetIndex = (firstUnreadIndex!! - (UnreadUiDefaults.VISIBLE_CONTEXT_COUNT - 1)).coerceAtLeast(0) + val index = firstUnreadIndex + if (index != null) { + val targetIndex = (index - (UnreadUiDefaults.VISIBLE_CONTEXT_COUNT - 1)).coerceAtLeast(0) listState.smartScrollToIndex(coroutineScope = coroutineScope, targetIndex = targetIndex) hasPerformedInitialScroll = true } else { diff --git a/feature/node/detekt-baseline.xml b/feature/node/detekt-baseline.xml index 5e7845d73..2465cc012 100644 --- a/feature/node/detekt-baseline.xml +++ b/feature/node/detekt-baseline.xml @@ -3,14 +3,10 @@ CyclomaticComplexMethod:CompassViewModel.kt$CompassViewModel$@Suppress("ReturnCount") private fun calculatePositionalAccuracyMeters(): Float? - CyclomaticComplexMethod:DeviceMetrics.kt$@Suppress("LongMethod") @Composable private fun DeviceMetricsChart( modifier: Modifier = Modifier, telemetries: List<Telemetry>, legendData: List<LegendData>, vicoScrollState: VicoScrollState, selectedX: Double?, onPointSelected: (Double) -> Unit, ) CyclomaticComplexMethod:NodeDetailActions.kt$NodeDetailActions$fun handleNodeMenuAction(scope: CoroutineScope, action: NodeMenuAction) CyclomaticComplexMethod:NodeDetailViewModel.kt$NodeDetailViewModel$fun handleNodeMenuAction(action: NodeMenuAction) - CyclomaticComplexMethod:PowerMetrics.kt$@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable private fun PowerMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick: () -> Unit) MagicNumber:MetricsViewModel.kt$MetricsViewModel$1000L MagicNumber:MetricsViewModel.kt$MetricsViewModel$1e-5 MagicNumber:MetricsViewModel.kt$MetricsViewModel$1e-7 - TooGenericExceptionCaught:PaxMetrics.kt$e: Exception - UnusedPrivateProperty:NodeDetailScreen.kt$val loadingMessage = stringResource(Res.string.loading) diff --git a/feature/settings/detekt-baseline.xml b/feature/settings/detekt-baseline.xml index 07bfecca3..21932a978 100644 --- a/feature/settings/detekt-baseline.xml +++ b/feature/settings/detekt-baseline.xml @@ -3,16 +3,10 @@ CyclomaticComplexMethod:DisplayConfigItemList.kt$@Composable fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) - CyclomaticComplexMethod:EditDeviceProfileDialog.kt$@Suppress("LongMethod") @OptIn(ExperimentalLayoutApi::class) @Composable fun EditDeviceProfileDialog( title: String, deviceProfile: DeviceProfile, onConfirm: (DeviceProfile) -> Unit, onDismiss: () -> Unit, modifier: Modifier = Modifier, ) CyclomaticComplexMethod:ExternalNotificationConfigItemList.kt$@Suppress("LongMethod", "TooGenericExceptionCaught") @Composable fun ExternalNotificationConfigScreen( onBack: () -> Unit, modifier: Modifier = Modifier, viewModel: RadioConfigViewModel = hiltViewModel(), ) CyclomaticComplexMethod:MQTTConfigItemList.kt$@Composable fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) CyclomaticComplexMethod:NetworkConfigItemList.kt$@Composable fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) - CyclomaticComplexMethod:PositionConfigItemList.kt$@OptIn(ExperimentalPermissionsApi::class) @Composable fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) - CyclomaticComplexMethod:RadioConfigViewModel.kt$RadioConfigViewModel$fun installProfile(protobuf: DeviceProfile) CyclomaticComplexMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshPacket) - CyclomaticComplexMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun setRemoteModuleConfig(destNum: Int, config: ModuleConfig) - CyclomaticComplexMethod:SecurityConfigItemList.kt$@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun SecurityConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) - LargeClass:RadioConfigViewModel.kt$RadioConfigViewModel : ViewModel LongMethod:AudioConfigItemList.kt$@Composable fun AudioConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) LongMethod:CannedMessageConfigItemList.kt$@Composable fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) LongMethod:DetectionSensorConfigItemList.kt$@Composable fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) @@ -20,10 +14,8 @@ LongMethod:DisplayConfigItemList.kt$@Composable fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) LongMethod:LoRaConfigItemList.kt$@Composable fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) LongMethod:NetworkConfigItemList.kt$@Composable fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) - LongMethod:PositionConfigItemList.kt$@OptIn(ExperimentalPermissionsApi::class) @Composable fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) LongMethod:PowerConfigItemList.kt$@Composable fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) LongMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshPacket) - LongMethod:SecurityConfigItemList.kt$@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun SecurityConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) LongMethod:SerialConfigItemList.kt$@Composable fun SerialConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) LongMethod:StoreForwardConfigItemList.kt$@Composable fun StoreForwardConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) LongMethod:TelemetryConfigItemList.kt$@Composable fun TelemetryConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) @@ -36,13 +28,10 @@ MagicNumber:EditDeviceProfileDialog.kt$ProfileField.FIXED_POSITION$6 MagicNumber:EditDeviceProfileDialog.kt$ProfileField.MODULE_CONFIG$5 MagicNumber:PacketResponseStateDialog.kt$100 - NestedBlockDepth:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshPacket) ReturnCount:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshPacket) TooGenericExceptionCaught:DebugViewModel.kt$DebugViewModel$e: Exception TooGenericExceptionCaught:LanguageUtils.kt$LanguageUtils$e: Exception TooGenericExceptionCaught:RadioConfigViewModel.kt$RadioConfigViewModel$ex: Exception TooManyFunctions:RadioConfigViewModel.kt$RadioConfigViewModel : ViewModel - UnusedPrivateMember:RadioConfigViewModel.kt$RadioConfigViewModel$private fun setChannels(channelUrl: String) - UnusedPrivateProperty:SettingsViewModel.kt$SettingsViewModel$val capabilities = Capabilities(node.metadata?.firmware_version) diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsViewModel.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsViewModel.kt index 6c48316b4..e609b2565 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsViewModel.kt @@ -31,6 +31,9 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import okio.BufferedSink +import okio.buffer +import okio.sink import org.meshtastic.core.common.BuildConfigProvider import org.meshtastic.core.common.database.DatabaseManager import org.meshtastic.core.domain.usecase.settings.ExportDataUseCase @@ -50,9 +53,8 @@ import org.meshtastic.core.repository.RadioConfigRepository import org.meshtastic.core.repository.UiPrefs import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed import org.meshtastic.proto.LocalConfig -import java.io.BufferedWriter import java.io.FileNotFoundException -import java.io.FileWriter +import java.io.FileOutputStream import javax.inject.Inject @Suppress("LongParameterList", "TooManyFunctions") @@ -176,12 +178,12 @@ constructor( } } - private suspend inline fun writeToUri(uri: Uri, crossinline block: suspend (BufferedWriter) -> Unit) { + private suspend inline fun writeToUri(uri: Uri, crossinline block: suspend (BufferedSink) -> Unit) { withContext(Dispatchers.IO) { try { app.contentResolver.openFileDescriptor(uri, "wt")?.use { parcelFileDescriptor -> - FileWriter(parcelFileDescriptor.fileDescriptor).use { fileWriter -> - BufferedWriter(fileWriter).use { writer -> block.invoke(writer) } + FileOutputStream(parcelFileDescriptor.fileDescriptor).sink().buffer().use { writer -> + block.invoke(writer) } } } catch (ex: FileNotFoundException) { diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt index 839e8d0e0..2756e8003 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt @@ -41,8 +41,10 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import okio.buffer +import okio.sink +import okio.source import org.jetbrains.compose.resources.StringResource -import org.meshtastic.core.data.repository.LocationRepository import org.meshtastic.core.domain.usecase.settings.AdminActionsUseCase import org.meshtastic.core.domain.usecase.settings.ExportProfileUseCase import org.meshtastic.core.domain.usecase.settings.ExportSecurityConfigUseCase @@ -60,6 +62,7 @@ import org.meshtastic.core.model.Position import org.meshtastic.core.navigation.SettingsRoutes import org.meshtastic.core.repository.AnalyticsPrefs import org.meshtastic.core.repository.HomoglyphPrefs +import org.meshtastic.core.repository.LocationRepository import org.meshtastic.core.repository.MapConsentPrefs import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.PacketRepository @@ -450,7 +453,7 @@ constructor( fun importProfile(uri: Uri, onResult: (DeviceProfile) -> Unit) = viewModelScope.launch(Dispatchers.IO) { try { - app.contentResolver.openInputStream(uri)?.use { inputStream -> + app.contentResolver.openInputStream(uri)?.source()?.buffer()?.use { inputStream -> importProfileUseCase(inputStream).onSuccess(onResult).onFailure { throw it } } } catch (ex: Exception) { @@ -463,7 +466,7 @@ constructor( withContext(Dispatchers.IO) { try { app.contentResolver.openFileDescriptor(uri, "wt")?.use { parcelFileDescriptor -> - FileOutputStream(parcelFileDescriptor.fileDescriptor).use { outputStream -> + FileOutputStream(parcelFileDescriptor.fileDescriptor).sink().buffer().use { outputStream -> exportProfileUseCase(outputStream, profile) .onSuccess { setResponseStateSuccess() } .onFailure { throw it } @@ -480,7 +483,7 @@ constructor( withContext(Dispatchers.IO) { try { app.contentResolver.openFileDescriptor(uri, "wt")?.use { parcelFileDescriptor -> - FileOutputStream(parcelFileDescriptor.fileDescriptor).use { outputStream -> + FileOutputStream(parcelFileDescriptor.fileDescriptor).sink().buffer().use { outputStream -> exportSecurityConfigUseCase(outputStream, securityConfig) .onSuccess { setResponseStateSuccess() } .onFailure { throw it } diff --git a/feature/settings/src/test/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModelTest.kt b/feature/settings/src/test/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModelTest.kt index b2067fbf2..676fb9a0c 100644 --- a/feature/settings/src/test/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModelTest.kt +++ b/feature/settings/src/test/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModelTest.kt @@ -33,7 +33,6 @@ import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test -import org.meshtastic.core.data.repository.LocationRepository import org.meshtastic.core.domain.usecase.settings.AdminActionsUseCase import org.meshtastic.core.domain.usecase.settings.ExportProfileUseCase import org.meshtastic.core.domain.usecase.settings.ExportSecurityConfigUseCase @@ -47,6 +46,7 @@ import org.meshtastic.core.domain.usecase.settings.ToggleHomoglyphEncodingUseCas import org.meshtastic.core.model.Node import org.meshtastic.core.repository.AnalyticsPrefs import org.meshtastic.core.repository.HomoglyphPrefs +import org.meshtastic.core.repository.LocationRepository import org.meshtastic.core.repository.MapConsentPrefs import org.meshtastic.core.repository.NodeRepository import org.meshtastic.core.repository.PacketRepository diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c0e8ae0c2..1acd59026 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -159,6 +159,7 @@ dokka-gradlePlugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", versi kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.4.0" } +kotlinx-atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version = "0.27.0" } kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines-android" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines-android" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines-android" } diff --git a/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainActivity.kt b/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainActivity.kt index c558de7e8..758e9c0b3 100644 --- a/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainActivity.kt +++ b/mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainActivity.kt @@ -134,7 +134,7 @@ class MainActivity : ComponentActivity() { Log.i(TAG, "Found service in package: ${serviceInfo.packageName}") } else { Log.w(TAG, "No service found for action com.geeksville.mesh.Service. Falling back to default.") - intent.setClassName("com.geeksville.mesh", "com.geeksville.mesh.service.MeshService") + intent.setClassName("com.geeksville.mesh", "org.meshtastic.app.service.MeshService") } val success = bindService(intent, serviceConnection, BIND_AUTO_CREATE) diff --git a/settings.gradle.kts b/settings.gradle.kts index 5b8062b06..b6f4a7467 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,7 +17,6 @@ include( ":app", - ":core:analytics", ":core:api", ":core:barcode", ":core:ble",