diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml index 8e02cdb26..4b051acb2 100644 --- a/app/detekt-baseline.xml +++ b/app/detekt-baseline.xml @@ -8,6 +8,7 @@ CommentWrapping:SignalMetrics.kt$Metric.SNR$/* Selected 12 as the max to get 4 equal vertical sections. */ ComposableNaming:NodeDetailScreen.kt$notesSection ComposableParamOrder:Channel.kt$ChannelScreen + ComposableParamOrder:Channel.kt$EditChannelUrl ComposableParamOrder:DeviceMetrics.kt$DeviceMetricsChart ComposableParamOrder:EmptyStateContent.kt$EmptyStateContent ComposableParamOrder:EnvironmentCharts.kt$ChartContent diff --git a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt b/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt index 4aece39f0..e64e1ad94 100644 --- a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt +++ b/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt @@ -19,9 +19,7 @@ package com.geeksville.mesh import android.app.Application import dagger.hilt.android.HiltAndroidApp -import org.meshtastic.core.analytics.platform.PlatformAnalytics import timber.log.Timber -import javax.inject.Inject /** * The main application class for Meshtastic. @@ -30,26 +28,7 @@ import javax.inject.Inject * application components, including analytics and platform-specific helpers, and manages analytics consent based on * user preferences. */ -@HiltAndroidApp -class MeshUtilApplication : Application() { - - @Inject lateinit var platformAnalytics: PlatformAnalytics - - companion object { - /** - * Provides access to the platform-specific analytics provider. Initialized via the injected [PlatformAnalytics] - * during [onCreate]. - */ - lateinit var analytics: PlatformAnalytics - private set - } - - override fun onCreate() { - super.onCreate() - // Initialize platform-specific features (analytics, crash reporting, etc.) - analytics = platformAnalytics - } -} +@HiltAndroidApp class MeshUtilApplication : Application() fun logAssert(executeReliableWrite: Boolean) { if (!executeReliableWrite) { diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index 8acdfd751..76cf72441 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -27,6 +27,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope +import androidx.navigation.NavHostController import com.geeksville.mesh.AdminProtos import com.geeksville.mesh.AppOnlyProtos import com.geeksville.mesh.ConfigProtos.Config @@ -55,6 +56,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch +import org.meshtastic.core.analytics.platform.PlatformAnalytics import org.meshtastic.core.data.repository.FirmwareReleaseRepository import org.meshtastic.core.data.repository.MeshLogRepository import org.meshtastic.core.data.repository.NodeRepository @@ -130,6 +132,7 @@ constructor( firmwareReleaseRepository: FirmwareReleaseRepository, private val uiPreferencesDataSource: UiPreferencesDataSource, private val meshServiceNotifications: MeshServiceNotifications, + private val analytics: PlatformAnalytics, ) : ViewModel() { val theme: StateFlow = uiPreferencesDataSource.theme @@ -351,4 +354,8 @@ constructor( fun onAppIntroCompleted() { uiPreferencesDataSource.setAppIntroCompleted(true) } + + fun addNavigationTrackingEffect(navController: NavHostController) { + analytics.addNavigationTrackingEffect(navController) + } } diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt index 94dd06dd5..2199f896d 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt @@ -24,7 +24,6 @@ import androidx.lifecycle.coroutineScope import com.geeksville.mesh.BuildConfig import com.geeksville.mesh.CoroutineDispatchers import com.geeksville.mesh.MeshProtos -import com.geeksville.mesh.MeshUtilApplication import com.geeksville.mesh.android.BinaryLogFile import com.geeksville.mesh.android.BuildUtils import com.geeksville.mesh.concurrent.handledLaunch @@ -46,6 +45,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import org.meshtastic.core.analytics.platform.PlatformAnalytics import org.meshtastic.core.model.util.anonymize import org.meshtastic.core.prefs.radio.RadioPrefs import org.meshtastic.core.service.ConnectionState @@ -74,6 +74,7 @@ constructor( private val processLifecycle: Lifecycle, private val radioPrefs: RadioPrefs, private val interfaceFactory: InterfaceFactory, + private val analytics: PlatformAnalytics, ) { private val _connectionState = MutableStateFlow(ConnectionState.DISCONNECTED) @@ -307,7 +308,7 @@ constructor( false } else { // Record that this use has configured a new radio - MeshUtilApplication.analytics.track("mesh_bond") + analytics.track("mesh_bond") // Ignore any errors that happen while closing old device ignoreException { stopInterface() } diff --git a/app/src/main/java/com/geeksville/mesh/ui/Main.kt b/app/src/main/java/com/geeksville/mesh/ui/Main.kt index 578689587..3810cdc9e 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Main.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Main.kt @@ -75,7 +75,6 @@ import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import com.geeksville.mesh.BuildConfig import com.geeksville.mesh.MeshProtos -import com.geeksville.mesh.MeshUtilApplication import com.geeksville.mesh.model.BTScanModel import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.navigation.channelsGraph @@ -158,7 +157,7 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: BTScanMode } } - MeshUtilApplication.analytics.addNavigationTrackingEffect(navController = navController) + uIViewModel.addNavigationTrackingEffect(navController) VersionChecks(uIViewModel) val alertDialogState by uIViewModel.currentAlert.collectAsStateWithLifecycle() diff --git a/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt b/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt index 97b5d4779..f039a037e 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt @@ -92,7 +92,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.geeksville.mesh.AppOnlyProtos.ChannelSet import com.geeksville.mesh.ChannelProtos import com.geeksville.mesh.ConfigProtos -import com.geeksville.mesh.MeshUtilApplication.Companion.analytics import com.geeksville.mesh.channelSet import com.geeksville.mesh.copy import com.geeksville.mesh.ui.common.components.ScannedQrCodeDialog @@ -102,7 +101,6 @@ import com.google.accompanist.permissions.rememberPermissionState import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanOptions import kotlinx.coroutines.launch -import org.meshtastic.core.analytics.DataPair import org.meshtastic.core.model.Channel import org.meshtastic.core.model.util.getChannelUrl import org.meshtastic.core.model.util.qrCode @@ -311,6 +309,7 @@ fun ChannelScreen( EditChannelUrl( enabled = enabled, channelUrl = selectedChannelSet.getChannelUrl(shouldAdd = shouldAddChannelsState), + onTrackShare = viewModel::trackShare, onConfirm = { viewModel.requestChannelUrl(it) { Toast.makeText(context, R.string.channel_invalid, Toast.LENGTH_SHORT).show() @@ -368,7 +367,13 @@ fun ChannelScreen( @Suppress("LongMethod") @Composable -private fun EditChannelUrl(enabled: Boolean, channelUrl: Uri, modifier: Modifier = Modifier, onConfirm: (Uri) -> Unit) { +private fun EditChannelUrl( + enabled: Boolean, + channelUrl: Uri, + modifier: Modifier = Modifier, + onTrackShare: () -> Unit, + onConfirm: (Uri) -> Unit, +) { val focusManager = LocalFocusManager.current val clipboardManager = LocalClipboard.current val coroutineScope = rememberCoroutineScope() @@ -416,7 +421,7 @@ private fun EditChannelUrl(enabled: Boolean, channelUrl: Uri, modifier: Modifier else -> { // track how many times users share channels - analytics.track("share", DataPair("content_type", "channel")) + onTrackShare() coroutineScope.launch { clipboardManager.setClipEntry( ClipEntry(ClipData.newPlainText(label, valueState.toString())), diff --git a/app/src/main/java/com/geeksville/mesh/ui/sharing/ChannelViewModel.kt b/app/src/main/java/com/geeksville/mesh/ui/sharing/ChannelViewModel.kt index 20ec42095..e7caee9c3 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/sharing/ChannelViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/sharing/ChannelViewModel.kt @@ -34,6 +34,8 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch +import org.meshtastic.core.analytics.DataPair +import org.meshtastic.core.analytics.platform.PlatformAnalytics import org.meshtastic.core.data.repository.RadioConfigRepository import org.meshtastic.core.model.util.toChannelSet import org.meshtastic.core.proto.getChannelList @@ -47,6 +49,7 @@ class ChannelViewModel constructor( private val serviceRepository: ServiceRepository, private val radioConfigRepository: RadioConfigRepository, + private val analytics: PlatformAnalytics, ) : ViewModel() { val connectionState = serviceRepository.connectionState @@ -121,6 +124,10 @@ constructor( } } + fun trackShare() { + analytics.track("share", DataPair("content_type", "channel")) + } + private inline fun updateLoraConfig(crossinline body: (Config.LoRaConfig) -> Config.LoRaConfig) { val data = body(localConfig.value.lora) setConfig(config { lora = data })