diff --git a/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/AndroidCompose.kt b/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/AndroidCompose.kt index d3cfceb2a..4169f8841 100644 --- a/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/AndroidCompose.kt +++ b/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/AndroidCompose.kt @@ -44,17 +44,16 @@ internal fun Project.configureAndroidCompose(commonExtension: CommonExtension) { "androidx.compose.runtime", "androidx.compose.ui", ) - - // The BOM exclusion above strips versions from transitive material deps - // (e.g. maps-compose-widgets, datadog). Pin the material group to the - // AndroidX version that matches this CMP release. + // The BOM exclusion above strips the version from `androidx.compose.material:material` + // requested by maps-compose-widgets (google flavor). Pin only that artifact — the + // group also contains `material-ripple`, which CMP publishes at the bom-aligned + // version and must not be force-downgraded. val materialVersion = libs.version("androidx-compose-material") - configurations.configureEach { resolutionStrategy.eachDependency { if (requested.group in cmpAlignedGroups) { useVersion(androidxComposeVersion) - } else if (requested.group == "androidx.compose.material") { + } else if (requested.group == "androidx.compose.material" && requested.name == "material") { useVersion(materialVersion) } } diff --git a/config/proguard/shared-rules.pro b/config/proguard/shared-rules.pro index 8d0d8efde..eb4ebd7fd 100644 --- a/config/proguard/shared-rules.pro +++ b/config/proguard/shared-rules.pro @@ -1,166 +1,135 @@ # ============================================================================ # Meshtastic — Shared ProGuard / R8 rules # ============================================================================ -# Cross-platform keep and dontwarn rules applied to BOTH the Android app -# release build (R8) and the Desktop distribution (ProGuard). Host-specific -# rules live in the per-module proguard-rules.pro file. +# Cross-platform keep rules applied to BOTH the Android app release (R8) and +# the Desktop distribution (ProGuard 7.7 invoked by compose-jb). # -# Rule of thumb: anything describing a library shared between Android and -# Desktop (Koin, kotlinx-serialization, Wire, Room KMP, Ktor, Coil 3, Kable, -# Kermit, Okio, DataStore, Paging, Lifecycle / Navigation 3, AboutLibraries, -# Markdown renderer, QRCode, Compose Multiplatform resources, core modules) -# belongs here. Anything platform-specific (AWT/Skiko/JNA, AIDL, Android -# framework, JDK-version quirks, flavor specifics) stays in the host file. +# IMPORTANT: compose-jb's standalone ProGuard task does NOT auto-include +# `META-INF/proguard/*.pro` consumer rules from dependency jars (only R8 on +# Android does — https://github.com/Guardsquare/proguard/issues/423). +# So this file inlines all the consumer rules we depend on for desktop. On +# Android these are duplicates of what R8 already auto-discovers, which is +# harmless. Per JetBrains compose-multiplatform docs: keep it as a single +# static .pro file and add rules as shrinking surfaces problems. # ============================================================================ # ---- Attributes ------------------------------------------------------------- +-keepattributes SourceFile,LineNumberTable,*Annotation*,Signature,InnerClasses,EnclosingMethod,Exceptions,RuntimeVisibleAnnotations,AnnotationDefault -# Preserve line numbers for meaningful stack traces, plus metadata needed for -# reflective serializer/DI/Room lookups. --keepattributes SourceFile,LineNumberTable,*Annotation*,Signature,InnerClasses,EnclosingMethod,Exceptions,RuntimeVisibleAnnotations +# ---- Compose Multiplatform 1.11 optimizer defense (#5146) ------------------- +# CMP 1.11 ships consumer rules with `-assumenosideeffects` on +# Composer.() / ComposerImpl.() and `-assumevalues` on +# ComposeRuntimeFlags / ComposeStackTraceMode. The primary defence is +# `-dontoptimize` (set per-host in app/desktop proguard-rules.pro), which +# disables rewriting of these directives. Broad package-wide keeps below have +# been removed per R8_Configuration_Analysis.md as they are redundant — rely +# instead on CMP's own consumer rules + @DoNotInline annotations. If animations +# freeze in a future CMP/KGP release, replace with class-level keeps on the +# specific failure points (Composer, ComposerImpl, ComposeRuntimeFlags, +# ComposeStackTraceMode) rather than package-wide wildcards. -# ---- Kotlin / Coroutines ---------------------------------------------------- -# Kotlin stdlib and kotlinx-coroutines ship their own consumer ProGuard rules -# (kotlin-stdlib and kotlinx-coroutines-core consumer-rules.pro) which keep -# Metadata, Continuation, kotlin.reflect internals, and debug metadata. No -# explicit wildcards needed here. +# ---- Compose Multiplatform resources ---------------------------------------- +-keep class org.meshtastic.core.resources.Res { *; } +-keepclassmembers class org.meshtastic.core.resources.Res$* { *; } -# ---- Koin DI (reflection-based injection) ----------------------------------- - -# Prevent R8 from merging exception classes (observed as io.ktor.http.URLDecodeException -# replacing Koin's InstanceCreationException in stack traces, making crashes -# undiagnosable). Broadened to all of koin core to cover the KSP-generated graph. --keep class org.koin.** { *; } --dontwarn org.koin.** - -# Keep Koin-annotated modules/components so Koin Annotations (KSP) output -# survives tree-shaking. +# ---- Koin Annotations (KSP-generated DI graph) ------------------------------ -keep @org.koin.core.annotation.Module class * { *; } -keep @org.koin.core.annotation.ComponentScan class * { *; } -keep @org.koin.core.annotation.Single class * { *; } -keep @org.koin.core.annotation.Factory class * { *; } -keep @org.koin.core.annotation.KoinViewModel class * { *; } -# ---- kotlinx-serialization -------------------------------------------------- +# ---- kotlinx.coroutines (inlined from coroutines.pro consumer rules) -------- +-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} +-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} +-keepclassmembers class kotlinx.coroutines.** { + volatile ; +} +-keepclassmembers class kotlin.coroutines.SafeContinuation { + volatile ; +} +-dontwarn java.lang.instrument.ClassFileTransformer +-dontwarn sun.misc.SignalHandler +-dontwarn java.lang.instrument.Instrumentation +-dontwarn sun.misc.Signal +-dontwarn java.lang.ClassValue +-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement --keep class kotlinx.serialization.** { *; } --dontwarn kotlinx.serialization.** - -# Keep @Serializable classes and their generated $serializer companions +# ---- kotlinx.serialization (inlined from kotlinx-serialization-common.pro) -- -keepclassmembers @kotlinx.serialization.Serializable class ** { static ** Companion; +} +-if @kotlinx.serialization.internal.NamedCompanion class * +-keepclassmembers class * { + static <1> *; +} +-if @kotlinx.serialization.Serializable class ** { + static **$* *; +} +-keepclassmembers class <2>$<3> { kotlinx.serialization.KSerializer serializer(...); } --keep class **.$serializer { *; } --keepclassmembers class **.$serializer { *; } --keepclasseswithmembers class ** { +-if @kotlinx.serialization.Serializable class ** { + public static ** INSTANCE; +} +-keepclassmembers class <1> { + public static <1> INSTANCE; kotlinx.serialization.KSerializer serializer(...); } +-dontnote kotlinx.serialization.** +-dontwarn kotlinx.serialization.internal.ClassValueReferences +-keepclassmembers public class **$$serializer { + private ** descriptor; +} -# ---- Wire Protobuf ---------------------------------------------------------- +# ---- kotlinx.datetime (inlined from datetime.pro consumer rules) ------------ +-dontwarn kotlinx.serialization.KSerializer +-dontwarn kotlinx.serialization.Serializable -# Wire generates an ADAPTER static field on every Message subclass accessed -# reflectively during encoding/decoding. Keep those fields and the -# ProtoAdapter subclasses themselves; Wire's bundled consumer rules preserve -# the runtime itself. +# ---- Ktor (inlined from ktor.pro + ServiceLoader gap) ----------------------- +-keepclassmembers class io.ktor.** { + volatile ; +} +-keepclassmembernames class io.ktor.** { + volatile ; +} +-keep class io.ktor.client.engine.** implements io.ktor.client.HttpClientEngineContainer +# ktor consumer rules preserve the ServiceLoader META-INF/services file but not +# the impl classes; ContentNegotiation discovers KotlinxSerializationJsonExtensionProvider +# reflectively and crashes with ServiceConfigurationError without these. +-keep class * implements io.ktor.serialization.kotlinx.KotlinxSerializationExtensionProvider { *; } +-keep class io.ktor.serialization.kotlinx.json.** { *; } + +# ---- androidx.annotation.Keep (inlined from androidx-annotations.pro) ------- +-keep,allowobfuscation @interface androidx.annotation.Keep +-keep @androidx.annotation.Keep class * {*;} +-keepclasseswithmembers class * { @androidx.annotation.Keep ; } +-keepclasseswithmembers class * { @androidx.annotation.Keep ; } +-keepclasseswithmembers class * { @androidx.annotation.Keep (...); } +-keepclassmembers,allowobfuscation class * { + @androidx.annotation.DoNotInline ; +} + +# ---- androidx.datastore (inlined from datastore-preferences-core.pro) ------- +-keepclassmembers class * extends androidx.datastore.preferences.protobuf.GeneratedMessageLite { + ; +} + +# ---- Wire Protobuf (no consumer rules shipped) ------------------------------ -keepclassmembers class * extends com.squareup.wire.Message { public static *** ADAPTER; } -keepclassmembers class * extends com.squareup.wire.ProtoAdapter { *; } -# Suppress warnings about missing Android Parcelable (Wire cross-platform stubs -# when compiling for non-Android JVM targets; harmless on Android). --dontwarn android.os.Parcel** --dontwarn android.os.Parcelable** +# ---- androidx.sqlite bundled driver (JNI native bridge) --------------------- +# androidx.sqlite-bundled's consumer rule keeps native methods only — but the +# bundled JNI library calls back into JVM methods on the driver class +# (e.g. `nativeThreadSafeMode`). Keep the whole driver package. +-keep class androidx.sqlite.driver.bundled.** { *; } +-keepclassmembers class androidx.sqlite.driver.bundled.** { native ; *; } # ---- Room KMP (room3) ------------------------------------------------------- - -# Preserve generated database constructors (Room uses reflection to instantiate) -keep class * extends androidx.room3.RoomDatabase { (); } -keep class * implements androidx.room3.RoomDatabaseConstructor { *; } - -# Keep the expect/actual MeshtasticDatabaseConstructor + database surface -keep class org.meshtastic.core.database.MeshtasticDatabaseConstructor { *; } -keep class org.meshtastic.core.database.MeshtasticDatabase { *; } - -# Room's own consumer rules (from androidx.room3) keep DAOs, entities, -# generated _Impl classes, and TypeConverters referenced from the database. - -# ---- SQLite bundled -------------------------------------------------------- -# androidx.sqlite ships consumer rules. - -# ---- Ktor (ServiceLoader + plugin discovery) -------------------------------- - -# Keep ServiceLoader metadata files (ktor discovers HttpClientEngineFactory -# implementations reflectively via ServiceLoader). --keepclassmembers class * implements io.ktor.client.HttpClientEngineFactory { *; } - -# ---- Coil 3 (image loading) ------------------------------------------------- -# coil3 ships consumer rules. - -# ---- Kable BLE -------------------------------------------------------------- -# com.juul.kable ships consumer rules; if release builds fail with missing -# Kable classes, restore a narrow keep for the specific reflection-loaded type. - -# ---- Compose Multiplatform resources ---------------------------------------- - -# Generated resource accessor classes (Res.string.*, Res.drawable.*, etc.). -# Without these the fdroid flavor has crashed at startup with a misleading -# URLDecodeException due to R8 exception-class merging. --keep class org.jetbrains.compose.resources.** { *; } --keep class org.meshtastic.core.resources.Res { *; } --keepclassmembers class org.meshtastic.core.resources.Res$* { *; } - -# ---- AboutLibraries --------------------------------------------------------- -# com.mikepenz.aboutlibraries ships consumer rules. - -# ---- Multiplatform Markdown Renderer ---------------------------------------- -# com.mikepenz.markdown ships consumer rules. - -# ---- QR Code Kotlin --------------------------------------------------------- - --keep class io.github.g0dkar.qrcode.** { *; } --dontwarn io.github.g0dkar.qrcode.** --keep class qrcode.** { *; } --dontwarn qrcode.** - -# ---- Kermit logging --------------------------------------------------------- -# co.touchlab.kermit ships consumer rules. - -# ---- Okio ------------------------------------------------------------------- -# okio ships consumer rules. - -# ---- DataStore -------------------------------------------------------------- -# androidx.datastore ships consumer rules. - -# ---- Paging ----------------------------------------------------------------- -# androidx.paging ships consumer rules. - -# ---- Lifecycle / Navigation 3 / ViewModel (JetBrains forks) ----------------- -# androidx.lifecycle and androidx.navigation3 ship consumer rules. - -# ---- Meshtastic shared model ------------------------------------------------ -# core.model types are reached via static references from Koin-wired graphs, -# Room entities, and kotlinx-serialization @Serializable companions — all of -# which have their own keep rules above. - -# ---- Compose Runtime & Animation -------------------------------------------- - -# Defence-in-depth: prevent tree-shaking of Compose infrastructure classes that -# are referenced indirectly through compiler-generated state machines. Applies -# to BOTH R8 (Android app) and ProGuard (desktop distribution). -# -# Why shared: CMP 1.11 ships consumer rules with -assumenosideeffects on -# Composer.() / ComposerImpl.() and -assumevalues on -# ComposeRuntimeFlags / ComposeStackTraceMode. If the optimizer runs (R8 full -# mode on Android, ProGuard with optimize.set(true) on desktop) these call -# sites can be rewritten even when the target classes are kept, causing the -# recomposer / frame-clock / animation state machines to silently freeze on -# the first frame. -dontoptimize (set per-host) is the primary defence; these -# keep rules are a safety net against future toolchain changes. See #5146. --keep class androidx.compose.runtime.** { *; } --keep class androidx.compose.ui.** { *; } --keep class androidx.compose.animation.core.** { *; } --keep class androidx.compose.animation.** { *; } --keep class androidx.compose.foundation.** { *; } --keep class androidx.compose.material3.** { *; } diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 58caf800b..2e66d6012 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -99,6 +99,12 @@ val generateBuildConfig = sourceSets.main { kotlin.srcDir(generateBuildConfig.map { buildConfigOutputDir }) } +// ── ProGuard configuration ─────────────────────────────────────────────────── +// compose-jb's standalone ProGuard 7.7 task does NOT auto-include +// `META-INF/proguard/*.pro` consumer rules from dependency jars (only R8 on +// Android does). We therefore inline every keep rule we need into the two +// static .pro files referenced below. + kotlin { jvmToolchain { languageVersion.set(JavaLanguageVersion.of(21)) diff --git a/desktop/proguard-rules.pro b/desktop/proguard-rules.pro index 9a9653e87..f02c2c6cd 100644 --- a/desktop/proguard-rules.pro +++ b/desktop/proguard-rules.pro @@ -1,113 +1,64 @@ # ============================================================================ # Meshtastic Desktop — ProGuard rules for release minification # ============================================================================ -# Open-source project: we rely on tree-shaking (unused code removal) for size -# reduction. Obfuscation is disabled in build.gradle.kts (obfuscate.set(false)). +# Open-source: obfuscation is OFF (build.gradle.kts: obfuscate.set(false)). +# Tree-shaking still runs. # -# Cross-platform library rules (Koin, kotlinx-serialization, Wire, Room, -# Ktor, Coil, Kable, Kermit, Okio, DataStore, Paging, Lifecycle, Navigation 3, -# AboutLibraries, Markdown, QRCode, CMP resources, core model) live in -# config/proguard/shared-rules.pro and are wired in by this module's -# build.gradle.kts. This file holds only desktop/JVM-specific rules. +# Two rule sources are merged into the ProGuard run: +# 1. JetBrains' bundled `default-compose-desktop-rules.pro` (auto-injected +# by the compose.desktop Gradle plugin). +# 2. Cross-platform project keeps in config/proguard/shared-rules.pro, +# which inlines every dependency consumer rule we need on desktop — +# compose-jb's standalone ProGuard task does NOT auto-discover +# `META-INF/proguard/*.pro` consumer rules from dependency jars (only +# R8 on Android does — https://github.com/Guardsquare/proguard/issues/423). +# +# This file only holds desktop/JVM-specific rules that aren't covered above. # ============================================================================ -# ---- General ---------------------------------------------------------------- - -# Suppress notes about duplicate resource files (common in fat JARs) --dontnote ** - -# Disable ProGuard optimization passes. Tree-shaking (unused code removal) still -# runs — only method-body rewrites and call-site transformations are suppressed. -# -# Why: CMP 1.11 ships consumer rules with -assumenosideeffects on -# Composer.() and ComposerImpl.(), plus -assumevalues on -# ComposeRuntimeFlags and ComposeStackTraceMode. These optimization directives -# let the optimizer rewrite *call sites* (class-init triggers, flag reads) even -# when the target classes are preserved by -keep rules. The result is that the -# Compose recomposer/frame-clock/animation state machines silently freeze on -# their first frame in release builds. -dontoptimize is the only directive that -# disables processing of -assumenosideeffects/-assumevalues. The desktop compose -# build sets optimize.set(true), so this applies here as well as to R8. See #5146. --dontoptimize - -# Do not parse/rewrite Kotlin metadata during shrinking/optimization. -# ProGuard's KotlinShrinker cannot handle the metadata produced by Compose -# Multiplatform 1.11.x + Kotlin 2.3.x, causing a NullPointerException. -# Since we disable obfuscation (class names remain stable), metadata references -# stay valid and do not need rewriting. The annotations themselves are preserved -# by -keepattributes *Annotation*. -# -# NOTE: -dontprocesskotlinmetadata is a ProGuard-only directive; R8 does not -# recognize it, which is why it lives in the desktop-only file. +# ---- ProGuard 7.7 + Kotlin 2.3 metadata workaround -------------------------- +# ProGuard 7.7's KotlinShrinker NPEs on metadata produced by CMP 1.11 + +# Kotlin 2.3.x. Because we don't obfuscate, class names stay stable and +# metadata references remain valid without rewriting. Annotations themselves +# are preserved by `-keepattributes *Annotation*` in shared-rules.pro. +# (R8-only directive equivalent does not exist; this is ProGuard-only.) -dontprocesskotlinmetadata +# ---- Disable optimizer (CMP 1.11 -assumenosideeffects defense) -------------- +# See shared-rules.pro for full rationale. Even though build.gradle.kts sets +# `optimize.set(true)` so compose-jb wires the optimization step, this rule +# turns it into a no-op — keeping CMP's `-assumenosideeffects` directives from +# rewriting Composer call sites and freezing the runtime. See #5146. +-dontoptimize + # ---- Entry point ------------------------------------------------------------ -# (org.meshtastic.desktop.MainKt is covered by the package-wide keep below.) - -# ---- Meshtastic desktop host shell ------------------------------------------ - -# Keep all desktop module classes (thin host shell — not worth tree-shaking) +# Keep the desktop host shell (thin module — not worth tree-shaking). -keep class org.meshtastic.desktop.** { *; } # ---- JVM runtime suppression ------------------------------------------------ - -dontwarn java.lang.reflect.** -dontwarn sun.misc.Unsafe -dontwarn java.lang.invoke.** -# ---- jSerialComm (cross-platform serial library with Android stubs) --------- - +# ---- jSerialComm Android stubs (cross-platform serial library) -------------- +# jSerialComm bundles Android shims that reference android.* classes; harmless +# on JVM/desktop but ProGuard fails the build on unresolved program classes +# unless suppressed. -dontwarn com.fazecast.jSerialComm.android.** -# ---- Kotlin stdlib atomics (Kotlin 2.3+ intrinsics, not on JDK 17) ---------- +# Wire ships AndroidMessage in its common runtime; on desktop classpath there is +# no android.os.Parcelable. We never use AndroidMessage on desktop. +-dontwarn com.squareup.wire.AndroidMessage +-dontwarn com.squareup.wire.AndroidMessage$* +-dontwarn android.os.Parcelable +-dontwarn android.os.Parcelable$* --dontwarn kotlin.concurrent.atomics.** --dontwarn kotlin.uuid.UuidV7Generator - -# ---- Library consumer rules ------------------------------------------------ -# The compose-jb gradle plugin auto-injects `default-compose-desktop-rules.pro` -# (bundled inside org.jetbrains.compose:compose-gradle-plugin) into every -# desktop ProGuard run. That file already covers: -# - kotlin.**, kotlinx.coroutines.** (incl. SwingDispatcherFactory ServiceLoader) -# - org.jetbrains.skiko.**, org.jetbrains.skia.** -# - kotlinx.serialization.** (incl. @Serializable companion keeps) -# - kotlinx.datetime.** -# - androidx.compose.runtime SnapshotStateKt + Material3 SliderDefaults -# So we DO NOT re-declare those here. Source of truth: -# https://github.com/JetBrains/compose-multiplatform/blob/master/gradle-plugins/compose/src/main/resources/default-compose-desktop-rules.pro -# -# However, the standalone ProGuard 7.7.0 that compose-jb invokes does NOT -# auto-import library `META-INF/proguard/*.pro` consumer rules from arbitrary -# jars (only R8/Android does). So any consumer-rule pattern outside the bundled -# defaults above must be copied here manually (see Ktor SL block below). - -# ---- androidx.sqlite bundled driver (JNI native bridge) --------------------- -# BundledSQLiteDriver loads `libsqliteJni` and the native code calls back into -# JVM-land via methods on `BundledSQLiteDriverKt` (e.g. `nativeThreadSafeMode`) -# and member methods on `BundledSQLiteDriver` itself. Because those JVM symbols -# are referenced only from native code, ProGuard removes them as unused; the -# native loader then crashes with `NoSuchMethodError: ... name or signature does -# not match`. Keep the whole driver package — it's small and entirely needed at -# runtime once the bundled SQLite driver is selected. --keep class androidx.sqlite.driver.bundled.** { *; } --keepclassmembers class androidx.sqlite.driver.bundled.** { native ; *; } - -# ---- Ktor serialization extension providers (ServiceLoader) ----------------- -# io.ktor.serialization.kotlinx-json discovers KotlinxSerializationJsonExtensionProvider -# via META-INF/services/io.ktor.serialization.kotlinx.KotlinxSerializationExtensionProvider. -# Without this keep the desktop HttpClient init throws ServiceConfigurationError -# at first request; on Windows jpackage's launcher swallows the trace and -# surfaces it as "Failed to launch JVM". --keep class * implements io.ktor.serialization.kotlinx.KotlinxSerializationExtensionProvider { *; } - -# ---- Vico 3.2.0-next.1 ColorScale (CMP API drift) --------------------------- -# Vico's new ColorScale* classes (ColorScaleShader, ColorScaleAreaFill, -# ColorScaleLineFill) reference CMP UI graphics members that don't exist in -# compose-multiplatform 1.11.0-beta03 (LinearGradientShader-VjE6UOU$default -# on ShaderKt and Paint.setShader(org.jetbrains.skia.Shader)). We don't use -# the ColorScale APIs in this app, so suppress these warnings to let ProGuard -# proceed; otherwise it aborts with "unresolved program class members". -# Remove once Vico ships a release built against CMP 1.11 stable. +# Vico's ColorScale* classes call into skia-shader bridges that aren't on the +# desktop ProGuard classpath. Vico ships no consumer rules. -dontwarn com.patrykandpatrick.vico.compose.cartesian.ColorScaleShader -dontwarn com.patrykandpatrick.vico.compose.cartesian.layer.ColorScaleAreaFill -dontwarn com.patrykandpatrick.vico.compose.cartesian.layer.ColorScaleLineFill + +# ---- Kotlin 2.3+ stdlib intrinsics not present on JDK 17 -------------------- +-dontwarn kotlin.concurrent.atomics.** +-dontwarn kotlin.uuid.UuidV7Generator diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 550d9dc20..09818fc27 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -44,9 +44,9 @@ compose-multiplatform-material3 = "1.11.0-alpha07" # AndroidCompose.kt's resolutionStrategy force-aligns these groups to *this* version # at resolution time, so it is the source of truth for the Android target. androidx-compose-bom-aligned = "1.11.0" -# `androidx-compose-material` (M2) is independent of CMP and pinned separately -# because some third-party libs (maps-compose-widgets, datadog) drag in -# unversioned material transitives. +# `androidx-compose-material` (M2) is independent of CMP. Pinned because +# maps-compose-widgets requests `androidx.compose.material:material` without +# a version (relying on a BOM that we exclude). M2 is frozen at 1.7.8. androidx-compose-material = "1.7.8" jetbrains-adaptive = "1.3.0-alpha07"