diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 2de575144..63815d478 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -12,14 +12,6 @@ concurrency: cancel-in-progress: true jobs: - test_secrets: - runs-on: ubuntu-latest - env: - TEST_SECRET: ${{ secrets.TEST_SECRET }} - steps: - - name: Test Secrets - run: | - echo "$TEST_SECRET" build_and_detekt: if: github.repository == 'meshtastic/Meshtastic-Android' && github.head_ref != 'scheduled-updates' diff --git a/.github/workflows/reusable-android-build.yml b/.github/workflows/reusable-android-build.yml index b9482718b..499452769 100644 --- a/.github/workflows/reusable-android-build.yml +++ b/.github/workflows/reusable-android-build.yml @@ -9,8 +9,6 @@ on: required: false DATADOG_CLIENT_TOKEN: required: false - TEST_SECRET: - required: false inputs: upload_artifacts: description: 'Whether to upload build and Detekt artifacts' @@ -27,10 +25,6 @@ jobs: DATADOG_CLIENT_TOKEN: ${{ secrets.DATADOG_CLIENT_TOKEN }} steps: - - name: Test Secrets - env: - TEST_SECRET: ${{ secrets.TEST_SECRET }} - run: echo "$TEST_SECRET" - name: Checkout code uses: actions/checkout@v4 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8ca48e692..fb1f0286b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -126,7 +126,7 @@ android { } } buildTypes { - named("release") { + release { if (keystoreProperties["storeFile"] != null) { signingConfig = signingConfigs.named("release").get() } @@ -134,7 +134,10 @@ android { isShrinkResources = true proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } - named("debug") { isPseudoLocalesEnabled = true } + debug { + isDebuggable = true + isPseudoLocalesEnabled = true + } } bundle { language { enableSplit = false } } buildFeatures { diff --git a/app/src/fdroid/java/com/geeksville/mesh/android/GeeksvilleApplication.kt b/app/src/fdroid/java/com/geeksville/mesh/android/GeeksvilleApplication.kt index 636391979..d728c1f02 100644 --- a/app/src/fdroid/java/com/geeksville/mesh/android/GeeksvilleApplication.kt +++ b/app/src/fdroid/java/com/geeksville/mesh/android/GeeksvilleApplication.kt @@ -22,10 +22,14 @@ import android.content.Context import android.content.SharedPreferences import android.provider.Settings import androidx.appcompat.app.AppCompatActivity +import androidx.compose.runtime.Composable import androidx.core.content.edit +import androidx.navigation.NavHostController import com.geeksville.mesh.BuildConfig import com.geeksville.mesh.analytics.AnalyticsProvider import com.geeksville.mesh.analytics.NopAnalytics +import com.geeksville.mesh.android.BuildUtils.debug +import com.geeksville.mesh.android.BuildUtils.info import com.geeksville.mesh.model.DeviceHardware import timber.log.Timber @@ -80,4 +84,13 @@ val Context.isGooglePlayAvailable: Boolean @Suppress("UnusedParameter") fun setAttributes(deviceVersion: String, deviceHardware: DeviceHardware) { // No-op for F-Droid version + info("Setting attributes: deviceVersion=$deviceVersion, deviceHardware=$deviceHardware") +} + +@Composable +fun AddNavigationTracking(navController: NavHostController) { + // No-op for F-Droid version + navController.addOnDestinationChangedListener { _, destination, _ -> + debug("Navigation changed to: ${destination.route}") + } } diff --git a/app/src/google/java/com/geeksville/mesh/android/GeeksvilleApplication.kt b/app/src/google/java/com/geeksville/mesh/android/GeeksvilleApplication.kt index f94275f46..a405229f6 100644 --- a/app/src/google/java/com/geeksville/mesh/android/GeeksvilleApplication.kt +++ b/app/src/google/java/com/geeksville/mesh/android/GeeksvilleApplication.kt @@ -21,11 +21,14 @@ import android.app.Application import android.content.Context import android.content.SharedPreferences import android.provider.Settings -import android.util.Log import androidx.appcompat.app.AppCompatActivity +import androidx.compose.runtime.Composable import androidx.core.content.edit +import androidx.navigation.NavHostController 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.compose.enableComposeActionTracking import com.datadog.android.core.configuration.Configuration import com.datadog.android.log.Logger @@ -35,7 +38,11 @@ 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.timber.DatadogTree +import com.datadog.android.trace.AndroidTracer +import com.datadog.android.trace.Trace +import com.datadog.android.trace.TraceConfiguration import com.geeksville.mesh.BuildConfig import com.geeksville.mesh.analytics.AnalyticsProvider import com.geeksville.mesh.analytics.FirebaseAnalytics @@ -44,6 +51,7 @@ import com.geeksville.mesh.util.exceptionReporter import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailabilityLight import com.suddenh4x.ratingdialog.AppRating +import io.opentracing.util.GlobalTracer import timber.log.Timber /** Created by kevinh on 1/4/15. */ @@ -135,10 +143,9 @@ open class GeeksvilleApplication : val logger = Logger.Builder() .setNetworkInfoEnabled(true) - .setLogcatLogsEnabled(true) .setRemoteSampleRate(sampleRate) .setBundleWithTraceEnabled(true) - .setName("TimberLogger") + .setBundleWithRumEnabled(true) .build() val configuration = Configuration.Builder( @@ -157,7 +164,6 @@ open class GeeksvilleApplication : TrackingConsent.NOT_GRANTED } Datadog.initialize(this, configuration, consent) - Datadog.setVerbosity(Log.VERBOSE) val rumConfiguration = RumConfiguration.Builder(BuildConfig.datadogApplicationId) @@ -166,7 +172,9 @@ open class GeeksvilleApplication : .trackFrustrations(true) .trackLongTasks() .trackNonFatalAnrs(true) - .trackUserInteractions() + // Re-enable tracking when auto instrumentation available. See note in `app/build.gradle` + .disableUserInteractionTracking() + // .trackUserInteractions() .enableComposeActionTracking() .build() Rum.enable(rumConfiguration) @@ -174,6 +182,12 @@ open class GeeksvilleApplication : val logsConfig = LogsConfiguration.Builder().build() Logs.enable(logsConfig) + val traceConfig = TraceConfiguration.Builder().build() + Trace.enable(traceConfig) + + val tracer = AndroidTracer.Builder().build() + GlobalTracer.registerIfAbsent(tracer) + Timber.plant(Timber.DebugTree(), DatadogTree(logger)) } } @@ -188,3 +202,13 @@ val Context.isGooglePlayAvailable: Boolean GoogleApiAvailabilityLight.getInstance().isGooglePlayServicesAvailable(this).let { it != ConnectionResult.SERVICE_MISSING && it != ConnectionResult.SERVICE_INVALID } + +@OptIn(ExperimentalTrackingApi::class) +@Composable +fun AddNavigationTracking(navController: NavHostController) { + NavigationViewTrackingEffect( + navController = navController, + trackArguments = true, + destinationPredicate = AcceptAllNavDestinations(), + ) +} 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 9e7b44ac4..7bd0edc1b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Main.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Main.kt @@ -82,6 +82,7 @@ import androidx.navigation.compose.rememberNavController import com.geeksville.mesh.BuildConfig import com.geeksville.mesh.MeshProtos import com.geeksville.mesh.R +import com.geeksville.mesh.android.AddNavigationTracking import com.geeksville.mesh.android.BuildUtils.debug import com.geeksville.mesh.android.setAttributes import com.geeksville.mesh.model.BluetoothViewModel @@ -159,6 +160,8 @@ fun MainScreen( } } + AddNavigationTracking(navController) + if (connectionState.isConnected()) { requestChannelSet?.let { newChannelSet -> ScannedQrCodeDialog(uIViewModel, newChannelSet) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0905e2393..cc53cc340 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -90,8 +90,10 @@ datastore-preferences = { group = "androidx.datastore", name = "datastore-prefer dd-sdk-android-compose = { group = "com.datadoghq", name = "dd-sdk-android-compose", version.ref = "dd-sdk-android" } dd-sdk-android-gradle-plugin = { group = "com.datadoghq", name = "dd-sdk-android-gradle-plugin", version.ref = "dd-sdk-android-gradle-plugin" } dd-sdk-android-logs = { group = "com.datadoghq", name = "dd-sdk-android-logs", version.ref = "dd-sdk-android" } +dd-sdk-android-okhttp = { group = "com.datadoghq", name = "dd-sdk-android-okhttp", version.ref = "dd-sdk-android" } dd-sdk-android-rum = { group = "com.datadoghq", name = "dd-sdk-android-rum", version.ref = "dd-sdk-android" } dd-sdk-android-timber = { group = "com.datadoghq", name = "dd-sdk-android-timber", version.ref = "dd-sdk-android" } +dd-sdk-android-trace = { group = "com.datadoghq", name = "dd-sdk-android-trace", version.ref = "dd-sdk-android" } detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" } emoji2-emojipicker = { group = "androidx.emoji2", name = "emoji2-emojipicker", version.ref = "emoji2" } espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } @@ -193,7 +195,7 @@ osm = ["osmdroid-android", "osmbonuspack", "mgrs"] firebase = ["firebase-analytics", "firebase-crashlytics"] # Datadog -datadog = ["dd-sdk-android-compose", "dd-sdk-android-logs", "dd-sdk-android-timber", "dd-sdk-android-rum"] +datadog = ["dd-sdk-android-compose", "dd-sdk-android-logs", "dd-sdk-android-okhttp", "dd-sdk-android-rum", "dd-sdk-android-timber", "dd-sdk-android-trace"] # Protobuf protobuf = ["protobuf-kotlin"] diff --git a/network/build.gradle.kts b/network/build.gradle.kts index 7ec160a63..e82f13a16 100644 --- a/network/build.gradle.kts +++ b/network/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { implementation(libs.bundles.hilt) implementation(libs.bundles.retrofit) implementation(libs.bundles.coil) + "googleImplementation"(libs.bundles.datadog) ksp(libs.hilt.compiler) implementation(libs.kotlinx.serialization.json) detektPlugins(libs.detekt.formatting) diff --git a/network/src/google/java/com/geeksville/mesh/network/di/ApiModule.kt b/network/src/google/java/com/geeksville/mesh/network/di/ApiModule.kt index 08bc01acf..7661d355a 100644 --- a/network/src/google/java/com/geeksville/mesh/network/di/ApiModule.kt +++ b/network/src/google/java/com/geeksville/mesh/network/di/ApiModule.kt @@ -26,6 +26,8 @@ import coil3.request.crossfade import coil3.svg.SvgDecoder import coil3.util.DebugLogger import coil3.util.Logger +import com.datadog.android.okhttp.DatadogEventListener +import com.datadog.android.okhttp.DatadogInterceptor import com.geeksville.mesh.network.BuildConfig import com.geeksville.mesh.network.retrofit.ApiService import dagger.Module @@ -49,13 +51,17 @@ class ApiModule { @Provides @Singleton fun provideOkHttpClient(): OkHttpClient { + val loggingInterceptor = HttpLoggingInterceptor().apply { if (BuildConfig.DEBUG) { setLevel(HttpLoggingInterceptor.Level.BODY) } } + val tracedHosts = listOf("meshtastic.org") return OkHttpClient.Builder() .addInterceptor(loggingInterceptor) + .addInterceptor(DatadogInterceptor.Builder(tracedHosts).build()) + .eventListenerFactory(DatadogEventListener.Factory()) .build() }