Merge "Add dedicated AppFunctionException subclasses for relevant error types" into androidx-main
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodPressureRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodPressureRecord.kt
index 38cb5b4..d2923d4 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodPressureRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodPressureRecord.kt
@@ -15,9 +15,11 @@
  */
 package androidx.health.connect.client.records
 
+import android.os.Build
 import androidx.annotation.IntDef
 import androidx.annotation.RestrictTo
 import androidx.health.connect.client.aggregate.AggregateMetric
+import androidx.health.connect.client.impl.platform.records.toPlatformRecord
 import androidx.health.connect.client.records.BloodPressureRecord.MeasurementLocation
 import androidx.health.connect.client.records.metadata.Metadata
 import androidx.health.connect.client.units.Pressure
@@ -60,11 +62,19 @@
     override val metadata: Metadata = Metadata.EMPTY,
 ) : InstantaneousRecord {
 
+    /*
+     * Android U devices and later use the platform's validation instead of Jetpack validation.
+     * See b/316852544 for more context.
+     */
     init {
-        systolic.requireNotLess(other = MIN_SYSTOLIC, name = "systolic")
-        systolic.requireNotMore(other = MAX_SYSTOLIC, name = "systolic")
-        diastolic.requireNotLess(other = MIN_DIASTOLIC, name = "diastolic")
-        diastolic.requireNotMore(other = MAX_DIASTOLIC, name = "diastolic")
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+            this.toPlatformRecord()
+        } else {
+            systolic.requireNotLess(other = MIN_SYSTOLIC, name = "systolic")
+            systolic.requireNotMore(other = MAX_SYSTOLIC, name = "systolic")
+            diastolic.requireNotLess(other = MIN_DIASTOLIC, name = "diastolic")
+            diastolic.requireNotMore(other = MAX_DIASTOLIC, name = "diastolic")
+        }
     }
 
     /*
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt
index 4dc9b41..1e1144e 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt
@@ -18,14 +18,104 @@
 
 import androidx.health.connect.client.records.metadata.Metadata
 import androidx.health.connect.client.units.Pressure
+import androidx.health.connect.client.units.millimetersOfMercury
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
+import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
 
 @RunWith(AndroidJUnit4::class)
 class BloodPressureRecordTest {
+
+    @Config(minSdk = 34)
+    @Test
+    fun constructor_paramsValidatedUsingPlatformValidation_createsBloodPressureRecord() {
+        assertThat(
+                BloodPressureRecord(
+                    time = Instant.ofEpochMilli(1234L),
+                    zoneOffset = null,
+                    systolic = 120.millimetersOfMercury,
+                    diastolic = 112.millimetersOfMercury,
+                    bodyPosition = BloodPressureRecord.BODY_POSITION_RECLINING,
+                    measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST,
+                    metadata = Metadata.EMPTY,
+                )
+            )
+            .isEqualTo(
+                BloodPressureRecord(
+                    time = Instant.ofEpochMilli(1234L),
+                    zoneOffset = null,
+                    systolic = 120.millimetersOfMercury,
+                    diastolic = 112.millimetersOfMercury,
+                    bodyPosition = BloodPressureRecord.BODY_POSITION_RECLINING,
+                    measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST,
+                    metadata = Metadata.EMPTY,
+                )
+            )
+    }
+
+    @Config(minSdk = 34)
+    @Test
+    fun constructor_paramsInvalidSystolicAndDiastolicValues_platformValidationFailsWithAnException() {
+        assertThrows(IllegalArgumentException::class.java) {
+            BloodPressureRecord(
+                time = Instant.ofEpochMilli(1234L),
+                zoneOffset = null,
+                systolic = 10.millimetersOfMercury,
+                diastolic = 500.millimetersOfMercury,
+                bodyPosition = BloodPressureRecord.BODY_POSITION_RECLINING,
+                measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST,
+                metadata = Metadata.EMPTY,
+            )
+        }
+    }
+
+    @Config(maxSdk = 33)
+    @Test
+    fun constructor_paramsValidatedUsingAPKValidation_createsBloodPressureRecord() {
+        assertThat(
+                BloodPressureRecord(
+                    time = Instant.ofEpochMilli(1234L),
+                    zoneOffset = null,
+                    systolic = 120.millimetersOfMercury,
+                    diastolic = 112.millimetersOfMercury,
+                    bodyPosition = BloodPressureRecord.BODY_POSITION_RECLINING,
+                    measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST,
+                    metadata = Metadata.EMPTY,
+                )
+            )
+            .isEqualTo(
+                BloodPressureRecord(
+                    time = Instant.ofEpochMilli(1234L),
+                    zoneOffset = null,
+                    systolic = 120.millimetersOfMercury,
+                    diastolic = 112.millimetersOfMercury,
+                    bodyPosition = BloodPressureRecord.BODY_POSITION_RECLINING,
+                    measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST,
+                    metadata = Metadata.EMPTY,
+                )
+            )
+    }
+
+    @Config(maxSdk = 33)
+    @Test
+    fun constructor_paramsInvalidSystolicAndDiastolicValues_apkValidationFailsWithAnException() {
+        assertThrows(IllegalArgumentException::class.java) {
+            BloodPressureRecord(
+                time = Instant.ofEpochMilli(1234L),
+                zoneOffset = null,
+                systolic = 10.millimetersOfMercury,
+                diastolic = 200.millimetersOfMercury,
+                bodyPosition = BloodPressureRecord.BODY_POSITION_RECLINING,
+                measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST,
+                metadata = Metadata.EMPTY,
+            )
+        }
+    }
+
     @Test
     fun bodyPositionEnums_existInMapping() {
         val allEnums = getAllIntDefEnums<BloodPressureRecord>("""BODY_POSITION.*(?<!UNKNOWN)$""")
diff --git a/libraryversions.toml b/libraryversions.toml
index 11546e0..0546928 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -19,7 +19,7 @@
 CAMERA_TESTING = "1.0.0-alpha01"
 CAMERA_VIEWFINDER = "1.4.0-alpha12"
 CARDVIEW = "1.1.0-alpha01"
-CAR_APP = "1.7.0-rc01"
+CAR_APP = "1.8.0-alpha01"
 COLLECTION = "1.5.0-beta02"
 COMPOSE = "1.8.0-alpha08"
 COMPOSE_MATERIAL3 = "1.4.0-alpha06"
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index 4e9ba65..df002aa 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -166,9 +166,10 @@
     method @androidx.compose.runtime.Composable public static void ButtonGroup(optional androidx.compose.ui.Modifier modifier, optional float spacing, optional float expansionWidth, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.wear.compose.material3.ButtonGroupScope,kotlin.Unit> content);
   }
 
-  public final class ButtonGroupScope {
-    ctor public ButtonGroupScope();
-    method public boolean buttonGroupItem(androidx.compose.foundation.interaction.InteractionSource interactionSource, optional float minWidth, optional float weight, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  public interface ButtonGroupScope {
+    method public androidx.compose.ui.Modifier enlargeOnPress(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method public androidx.compose.ui.Modifier minWidth(androidx.compose.ui.Modifier, optional float minWidth);
+    method public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, fromInclusive=false) float weight);
   }
 
   public final class ButtonKt {
@@ -477,7 +478,7 @@
   }
 
   public final class DatePickerKt {
-    method @androidx.compose.runtime.Composable public static void DatePicker(java.time.LocalDate initialDate, kotlin.jvm.functions.Function1<? super java.time.LocalDate,kotlin.Unit> onDatePicked, optional androidx.compose.ui.Modifier modifier, optional java.time.LocalDate? minValidDate, optional java.time.LocalDate? maxValidDate, optional int datePickerType, optional androidx.wear.compose.material3.DatePickerColors colors);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) @androidx.compose.runtime.Composable public static void DatePicker(java.time.LocalDate initialDate, kotlin.jvm.functions.Function1<? super java.time.LocalDate,kotlin.Unit> onDatePicked, optional androidx.compose.ui.Modifier modifier, optional java.time.LocalDate? minValidDate, optional java.time.LocalDate? maxValidDate, optional int datePickerType, optional androidx.wear.compose.material3.DatePickerColors colors);
   }
 
   @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class DatePickerType {
@@ -1626,7 +1627,7 @@
   }
 
   public final class TimePickerKt {
-    method @androidx.compose.runtime.Composable public static void TimePicker(java.time.LocalTime initialTime, kotlin.jvm.functions.Function1<? super java.time.LocalTime,kotlin.Unit> onTimePicked, optional androidx.compose.ui.Modifier modifier, optional int timePickerType, optional androidx.wear.compose.material3.TimePickerColors colors);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) @androidx.compose.runtime.Composable public static void TimePicker(java.time.LocalTime initialTime, kotlin.jvm.functions.Function1<? super java.time.LocalTime,kotlin.Unit> onTimePicked, optional androidx.compose.ui.Modifier modifier, optional int timePickerType, optional androidx.wear.compose.material3.TimePickerColors colors);
   }
 
   @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class TimePickerType {
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index 4e9ba65..df002aa 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -166,9 +166,10 @@
     method @androidx.compose.runtime.Composable public static void ButtonGroup(optional androidx.compose.ui.Modifier modifier, optional float spacing, optional float expansionWidth, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.wear.compose.material3.ButtonGroupScope,kotlin.Unit> content);
   }
 
-  public final class ButtonGroupScope {
-    ctor public ButtonGroupScope();
-    method public boolean buttonGroupItem(androidx.compose.foundation.interaction.InteractionSource interactionSource, optional float minWidth, optional float weight, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  public interface ButtonGroupScope {
+    method public androidx.compose.ui.Modifier enlargeOnPress(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method public androidx.compose.ui.Modifier minWidth(androidx.compose.ui.Modifier, optional float minWidth);
+    method public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, fromInclusive=false) float weight);
   }
 
   public final class ButtonKt {
@@ -477,7 +478,7 @@
   }
 
   public final class DatePickerKt {
-    method @androidx.compose.runtime.Composable public static void DatePicker(java.time.LocalDate initialDate, kotlin.jvm.functions.Function1<? super java.time.LocalDate,kotlin.Unit> onDatePicked, optional androidx.compose.ui.Modifier modifier, optional java.time.LocalDate? minValidDate, optional java.time.LocalDate? maxValidDate, optional int datePickerType, optional androidx.wear.compose.material3.DatePickerColors colors);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) @androidx.compose.runtime.Composable public static void DatePicker(java.time.LocalDate initialDate, kotlin.jvm.functions.Function1<? super java.time.LocalDate,kotlin.Unit> onDatePicked, optional androidx.compose.ui.Modifier modifier, optional java.time.LocalDate? minValidDate, optional java.time.LocalDate? maxValidDate, optional int datePickerType, optional androidx.wear.compose.material3.DatePickerColors colors);
   }
 
   @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class DatePickerType {
@@ -1626,7 +1627,7 @@
   }
 
   public final class TimePickerKt {
-    method @androidx.compose.runtime.Composable public static void TimePicker(java.time.LocalTime initialTime, kotlin.jvm.functions.Function1<? super java.time.LocalTime,kotlin.Unit> onTimePicked, optional androidx.compose.ui.Modifier modifier, optional int timePickerType, optional androidx.wear.compose.material3.TimePickerColors colors);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) @androidx.compose.runtime.Composable public static void TimePicker(java.time.LocalTime initialTime, kotlin.jvm.functions.Function1<? super java.time.LocalTime,kotlin.Unit> onTimePicked, optional androidx.compose.ui.Modifier modifier, optional int timePickerType, optional androidx.wear.compose.material3.TimePickerColors colors);
   }
 
   @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class TimePickerType {
diff --git a/wear/compose/compose-material3/benchmark/build.gradle b/wear/compose/compose-material3/benchmark/build.gradle
index 9a1b2f5..836aa4e 100644
--- a/wear/compose/compose-material3/benchmark/build.gradle
+++ b/wear/compose/compose-material3/benchmark/build.gradle
@@ -35,7 +35,7 @@
     compileSdk = 35
 
     defaultConfig {
-        minSdk = 30
+        minSdk = 25
     }
     buildTypes.configureEach {
         consumerProguardFiles "benchmark-proguard-rules.pro"
diff --git a/wear/compose/compose-material3/build.gradle b/wear/compose/compose-material3/build.gradle
index df4dcfa..f0c49b7 100644
--- a/wear/compose/compose-material3/build.gradle
+++ b/wear/compose/compose-material3/build.gradle
@@ -72,7 +72,7 @@
     compileSdk = 35
 
     defaultConfig {
-        minSdk = 30
+        minSdk = 25
     }
     // Use Robolectric 4.+
     testOptions.unitTests.includeAndroidResources = true
diff --git a/wear/compose/compose-material3/integration-tests/build.gradle b/wear/compose/compose-material3/integration-tests/build.gradle
index 85e2742..956afb7 100644
--- a/wear/compose/compose-material3/integration-tests/build.gradle
+++ b/wear/compose/compose-material3/integration-tests/build.gradle
@@ -55,7 +55,7 @@
 android {
     compileSdk = 35
     defaultConfig {
-        minSdk = 30
+        minSdk = 25
     }
     namespace = "androidx.wear.compose.material3.demos"
 }
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonGroupDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonGroupDemo.kt
index a12ca7e..76c8ba5 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonGroupDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonGroupDemo.kt
@@ -18,33 +18,161 @@
 
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.wear.compose.material3.Button
 import androidx.wear.compose.material3.ButtonGroup
+import androidx.wear.compose.material3.ButtonGroupScope
+import androidx.wear.compose.material3.FilledIconButton
+import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.IconToggleButton
+import androidx.wear.compose.material3.IconToggleButtonDefaults
+import androidx.wear.compose.material3.IconToggleButtonShapes
 import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.TextToggleButton
+import androidx.wear.compose.material3.TextToggleButtonDefaults
+import androidx.wear.compose.material3.TextToggleButtonShapes
+import androidx.wear.compose.material3.samples.icons.WifiOffIcon
+import androidx.wear.compose.material3.samples.icons.WifiOnIcon
 
 @Composable
 fun ButtonGroupDemo() {
-    val interactionSources = remember { Array(3) { MutableInteractionSource() } }
-
+    val interactionSource1 = remember { MutableInteractionSource() }
+    val interactionSource2 = remember { MutableInteractionSource() }
+    val interactionSource3 = remember { MutableInteractionSource() }
     Box(Modifier.size(300.dp), contentAlignment = Alignment.Center) {
         ButtonGroup(Modifier.fillMaxWidth()) {
-            repeat(3) { index ->
-                buttonGroupItem(interactionSource = interactionSources[index]) {
-                    Button(onClick = {}, interactionSource = interactionSources[index]) {
-                        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
-                            Text(listOf("A", "B", "C")[index])
-                        }
+            Button(
+                onClick = {},
+                Modifier.enlargeOnPress(interactionSource1),
+                interactionSource = interactionSource1
+            ) {
+                Text("<", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
+            }
+            FilledIconButton(
+                onClick = {},
+                Modifier.enlargeOnPress(interactionSource2),
+                interactionSource = interactionSource2
+            ) {
+                Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                    Icon(
+                        imageVector = Icons.Filled.Favorite,
+                        contentDescription = "Favorite icon",
+                        modifier = Modifier.size(32.dp)
+                    )
+                }
+            }
+            Button(
+                onClick = {},
+                Modifier.enlargeOnPress(interactionSource3),
+                interactionSource = interactionSource3
+            ) {
+                Text(">", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
+            }
+        }
+    }
+}
+
+@Composable
+fun ButtonGroupToggleButtonsDemo() {
+    val iconSize = 32.dp
+    Box(Modifier.size(300.dp), contentAlignment = Alignment.Center) {
+        Column {
+            ButtonGroup(Modifier.fillMaxWidth()) {
+                MyIconToggleButton(IconToggleButtonDefaults.shapes(), Modifier.weight(1.2f)) {
+                    Icon(
+                        imageVector = Icons.Filled.Favorite,
+                        contentDescription = "Favorite icon",
+                        modifier = Modifier.size(iconSize)
+                    )
+                }
+                MyIconToggleButton(IconToggleButtonDefaults.animatedShapes()) { checked ->
+                    if (checked) {
+                        WifiOnIcon(Modifier.size(iconSize))
+                    } else {
+                        WifiOffIcon(Modifier.size(iconSize))
                     }
                 }
             }
+            Spacer(Modifier.height(8.dp))
+            ButtonGroup(Modifier.fillMaxWidth()) {
+                MyTextToggleButton(TextToggleButtonDefaults.shapes()) { checked ->
+                    Text(
+                        text = if (checked) "On" else "Off",
+                        style = TextToggleButtonDefaults.defaultButtonTextStyle
+                    )
+                }
+                MyTextToggleButton(
+                    TextToggleButtonDefaults.animatedShapes(),
+                    Modifier.weight(1.2f)
+                ) { checked ->
+                    Text(
+                        text = if (checked) "On" else "Off",
+                        style = TextToggleButtonDefaults.defaultButtonTextStyle
+                    )
+                }
+            }
         }
     }
 }
+
+@Composable
+private fun ButtonGroupScope.MyIconToggleButton(
+    shapes: IconToggleButtonShapes,
+    modifier: Modifier = Modifier,
+    content: @Composable (Boolean) -> Unit
+) {
+    val interactionSource = remember { MutableInteractionSource() }
+    var checked by remember { mutableStateOf(false) }
+    IconToggleButton(
+        checked = checked,
+        modifier =
+            modifier
+                .height(IconToggleButtonDefaults.SmallButtonSize)
+                .fillMaxWidth()
+                .enlargeOnPress(interactionSource),
+        onCheckedChange = { checked = !checked },
+        shapes = shapes,
+        interactionSource = interactionSource
+    ) {
+        content(checked)
+    }
+}
+
+@Composable
+private fun ButtonGroupScope.MyTextToggleButton(
+    shapes: TextToggleButtonShapes,
+    modifier: Modifier = Modifier,
+    content: @Composable (Boolean) -> Unit
+) {
+    val interactionSource = remember { MutableInteractionSource() }
+    var checked by remember { mutableStateOf(false) }
+    TextToggleButton(
+        checked = checked,
+        modifier =
+            modifier
+                .height(TextToggleButtonDefaults.DefaultButtonSize)
+                .fillMaxWidth()
+                .enlargeOnPress(interactionSource),
+        onCheckedChange = { checked = !checked },
+        shapes = shapes,
+        interactionSource = interactionSource
+    ) {
+        content(checked)
+    }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/DatePickerDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/DatePickerDemo.kt
index a80f168..2e020d7 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/DatePickerDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/DatePickerDemo.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3.demos
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.material.icons.Icons
@@ -41,6 +43,7 @@
 import java.time.format.DateTimeFormatter
 import java.time.format.FormatStyle
 
+@RequiresApi(Build.VERSION_CODES.O)
 val DatePickerDemos =
     listOf(
         ComposableDemo("Date Year-Month-Day") { DatePickerYearMonthDaySample() },
@@ -51,6 +54,7 @@
         ComposableDemo("Past only") { DatePickerPastOnlyDemo() },
     )
 
+@RequiresApi(Build.VERSION_CODES.O)
 @Composable
 fun DatePickerDemo(datePickerType: DatePickerType) {
     var showDatePicker by remember { mutableStateOf(true) }
@@ -85,6 +89,7 @@
     }
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 @Composable
 fun DatePickerPastOnlyDemo() {
     val currentDate = LocalDate.now()
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TimePickerDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TimePickerDemo.kt
index 7a1f55b..20d4ca7 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TimePickerDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TimePickerDemo.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3.demos
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.material.icons.Icons
@@ -39,6 +41,7 @@
 import java.time.LocalTime
 import java.time.format.DateTimeFormatter
 
+@RequiresApi(Build.VERSION_CODES.O)
 val TimePickerDemos =
     listOf(
         ComposableDemo("Time HH:MM:SS") { TimePickerWithSecondsSample() },
@@ -47,6 +50,7 @@
         ComposableDemo("Time System time format") { TimePickerSample() },
     )
 
+@RequiresApi(Build.VERSION_CODES.O)
 @Composable
 private fun TimePicker24hWithoutSecondsDemo() {
     var showTimePicker by remember { mutableStateOf(true) }
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TransformingLazyColumnDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TransformingLazyColumnDemo.kt
index 4523210..4b6646b 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TransformingLazyColumnDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TransformingLazyColumnDemo.kt
@@ -154,21 +154,25 @@
         }
         item {
             TransformExclusion {
-                val interactionSourceLeft = remember { MutableInteractionSource() }
-                val interactionSourceRight = remember { MutableInteractionSource() }
+                val interactionSource1 = remember { MutableInteractionSource() }
+                val interactionSource2 = remember { MutableInteractionSource() }
                 ButtonGroup(Modifier.scrollTransform(this@item)) {
-                    buttonGroupItem(interactionSource = interactionSourceLeft) {
-                        Button(onClick = {}, interactionSource = interactionSourceLeft) {
-                            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
-                                Text("L")
-                            }
+                    Button(
+                        onClick = {},
+                        Modifier.enlargeOnPress(interactionSource1),
+                        interactionSource = interactionSource1
+                    ) {
+                        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                            Text("L")
                         }
                     }
-                    buttonGroupItem(interactionSource = interactionSourceRight) {
-                        Button(onClick = {}, interactionSource = interactionSourceRight) {
-                            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
-                                Text("R")
-                            }
+                    Button(
+                        onClick = {},
+                        Modifier.enlargeOnPress(interactionSource2),
+                        interactionSource = interactionSource2
+                    ) {
+                        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                            Text("R")
                         }
                     }
                 }
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
index 8a93dff..11844f9 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
@@ -26,6 +26,7 @@
 import androidx.wear.compose.material3.samples.AnimatedTextSampleButtonResponse
 import androidx.wear.compose.material3.samples.AnimatedTextSampleSharedFontRegistry
 import androidx.wear.compose.material3.samples.ButtonGroupSample
+import androidx.wear.compose.material3.samples.ButtonGroupThreeButtonsSample
 import androidx.wear.compose.material3.samples.EdgeButtonListSample
 import androidx.wear.compose.material3.samples.EdgeButtonSample
 import androidx.wear.compose.material3.samples.EdgeSwipeForSwipeToDismiss
@@ -113,7 +114,9 @@
                     "Button Group",
                     listOf(
                         ComposableDemo("Two buttons") { ButtonGroupSample() },
-                        ComposableDemo("Three buttons") { ButtonGroupDemo() },
+                        ComposableDemo("ABC") { ButtonGroupThreeButtonsSample() },
+                        ComposableDemo("Text And Icon") { ButtonGroupDemo() },
+                        ComposableDemo("ToggleButtons") { ButtonGroupToggleButtonsDemo() },
                     )
                 ),
                 ComposableDemo("List Header") { Centralize { ListHeaderSample() } },
@@ -138,8 +141,13 @@
                 Material3DemoCategory("Stepper", StepperDemos),
                 Material3DemoCategory("Slider", SliderDemos),
                 Material3DemoCategory("Picker", PickerDemos),
-                Material3DemoCategory("TimePicker", TimePickerDemos),
-                Material3DemoCategory("DatePicker", DatePickerDemos),
+                // Requires API level 26 or higher due to java.time dependency.
+                *(if (Build.VERSION.SDK_INT >= 26)
+                    arrayOf(
+                        Material3DemoCategory("TimePicker", TimePickerDemos),
+                        Material3DemoCategory("DatePicker", DatePickerDemos)
+                    )
+                else emptyArray<Material3DemoCategory>()),
                 Material3DemoCategory("Progress Indicator", ProgressIndicatorDemos),
                 Material3DemoCategory("Scroll Indicator", ScrollIndicatorDemos),
                 Material3DemoCategory("Placeholder", PlaceholderDemos),
diff --git a/wear/compose/compose-material3/macrobenchmark-common/build.gradle b/wear/compose/compose-material3/macrobenchmark-common/build.gradle
index 51008d8..92093e3 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/build.gradle
+++ b/wear/compose/compose-material3/macrobenchmark-common/build.gradle
@@ -10,7 +10,7 @@
     compileSdk = 35
 
     defaultConfig {
-        minSdk = 30
+        minSdk = 25
     }
 
     buildTypes.configureEach {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/ButtonGroupBenchmark.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/ButtonGroupBenchmark.kt
index bfbc1a9..2d5a664 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/ButtonGroupBenchmark.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/ButtonGroupBenchmark.kt
@@ -18,14 +18,12 @@
 
 import android.os.SystemClock
 import androidx.benchmark.macro.MacrobenchmarkScope
-import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.semantics.contentDescription
@@ -40,30 +38,22 @@
 object ButtonGroupBenchmark : MacrobenchmarkScreen {
     override val content: @Composable (BoxScope.() -> Unit)
         get() = {
-            val interactionSourceLeft = remember { MutableInteractionSource() }
-            val interactionSourceRight = remember { MutableInteractionSource() }
             Box(Modifier.size(300.dp), contentAlignment = Alignment.Center) {
                 ButtonGroup(Modifier.fillMaxWidth()) {
-                    buttonGroupItem(interactionSource = interactionSourceLeft) {
-                        Button(
-                            modifier = Modifier.semantics { contentDescription = LEFT_BUTTON },
-                            onClick = {},
-                            interactionSource = interactionSourceLeft
-                        ) {
-                            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
-                                Text("Left")
-                            }
+                    Button(
+                        modifier = Modifier.semantics { contentDescription = LEFT_BUTTON },
+                        onClick = {},
+                    ) {
+                        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                            Text("Left")
                         }
                     }
-                    buttonGroupItem(interactionSource = interactionSourceRight) {
-                        Button(
-                            modifier = Modifier.semantics { contentDescription = RIGHT_BUTTON },
-                            onClick = {},
-                            interactionSource = interactionSourceRight
-                        ) {
-                            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
-                                Text("Right")
-                            }
+                    Button(
+                        modifier = Modifier.semantics { contentDescription = RIGHT_BUTTON },
+                        onClick = {},
+                    ) {
+                        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                            Text("Right")
                         }
                     }
                 }
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/DatePickerBenchmark.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/DatePickerBenchmark.kt
index 3177504..51431d0 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/DatePickerBenchmark.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/DatePickerBenchmark.kt
@@ -16,7 +16,9 @@
 
 package androidx.wear.compose.material3.macrobenchmark.common
 
+import android.os.Build
 import android.os.SystemClock
+import androidx.annotation.RequiresApi
 import androidx.benchmark.macro.MacrobenchmarkScope
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.material.icons.Icons
@@ -41,6 +43,7 @@
 import java.time.format.DateTimeFormatter
 import java.time.format.FormatStyle
 
+@RequiresApi(Build.VERSION_CODES.O)
 object DatePickerBenchmark : MacrobenchmarkScreen {
     override val content: @Composable (BoxScope.() -> Unit)
         get() = {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TimePickerBenchmark.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TimePickerBenchmark.kt
index 1c9103b..7d15ce7 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TimePickerBenchmark.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TimePickerBenchmark.kt
@@ -16,7 +16,9 @@
 
 package androidx.wear.compose.material3.macrobenchmark.common
 
+import android.os.Build
 import android.os.SystemClock
+import androidx.annotation.RequiresApi
 import androidx.benchmark.macro.MacrobenchmarkScope
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
@@ -24,6 +26,7 @@
 import androidx.wear.compose.material3.TimePickerType
 import java.time.LocalTime
 
+@RequiresApi(Build.VERSION_CODES.O)
 object TimePickerBenchmark : MacrobenchmarkScreen {
     override val content: @Composable (BoxScope.() -> Unit)
         get() = {
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/DatePickerScreen.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/DatePickerScreen.kt
index 8e3685d1..04e9ca75 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/DatePickerScreen.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/baselineprofile/DatePickerScreen.kt
@@ -16,6 +16,7 @@
 
 package androidx.wear.compose.material3.macrobenchmark.common.baselineprofile
 
+import android.os.Build
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
 import androidx.wear.compose.material3.DatePicker
@@ -27,14 +28,16 @@
     object : MacrobenchmarkScreen {
         override val content: @Composable BoxScope.() -> Unit
             get() = {
-                val minDate = LocalDate.of(2022, 10, 15)
-                val maxDate = LocalDate.of(2025, 2, 4)
-                DatePicker(
-                    initialDate = LocalDate.of(2024, 9, 2),
-                    onDatePicked = {},
-                    minValidDate = minDate,
-                    maxValidDate = maxDate,
-                    datePickerType = DatePickerType.YearMonthDay
-                )
+                if (Build.VERSION.SDK_INT >= 26) {
+                    val minDate = LocalDate.of(2022, 10, 15)
+                    val maxDate = LocalDate.of(2025, 2, 4)
+                    DatePicker(
+                        initialDate = LocalDate.of(2024, 9, 2),
+                        onDatePicked = {},
+                        minValidDate = minDate,
+                        maxValidDate = maxDate,
+                        datePickerType = DatePickerType.YearMonthDay
+                    )
+                }
             }
     }
diff --git a/wear/compose/compose-material3/macrobenchmark-target/build.gradle b/wear/compose/compose-material3/macrobenchmark-target/build.gradle
index eaba1e7..82913ef 100644
--- a/wear/compose/compose-material3/macrobenchmark-target/build.gradle
+++ b/wear/compose/compose-material3/macrobenchmark-target/build.gradle
@@ -46,4 +46,4 @@
     implementation(project(":wear:compose:compose-material3-macrobenchmark-common"))
 }
 
-android.defaultConfig.minSdk = 30
+android.defaultConfig.minSdk = 25
diff --git a/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml b/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml
index a68fd42..6ec1884 100644
--- a/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -130,7 +130,8 @@
         <activity
             android:name=".DatePickerActivity"
             android:theme="@style/AppTheme"
-            android:exported="true">
+            android:exported="true"
+            tools:targetApi="o">
             <intent-filter>
                 <action android:name=
                     "androidx.wear.compose.material3.macrobenchmark.target.DATE_PICKER_ACTIVITY" />
@@ -269,7 +270,8 @@
         <activity
             android:name=".TimePickerActivity"
             android:theme="@style/AppTheme"
-            android:exported="true">
+            android:exported="true"
+            tools:targetApi="o">
             <intent-filter>
                 <action android:name=
                     "androidx.wear.compose.material3.macrobenchmark.target.TIME_PICKER_ACTIVITY" />
diff --git a/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/DatePickerActivity.kt b/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/DatePickerActivity.kt
index 4256fdc..d18d12d 100644
--- a/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/DatePickerActivity.kt
+++ b/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/DatePickerActivity.kt
@@ -16,6 +16,9 @@
 
 package androidx.wear.compose.material3.macrobenchmark.target
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.wear.compose.material3.macrobenchmark.common.DatePickerBenchmark
 
+@RequiresApi(Build.VERSION_CODES.O)
 class DatePickerActivity : BenchmarkBaseActivity(DatePickerBenchmark)
diff --git a/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/TimePickerActivity.kt b/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/TimePickerActivity.kt
index 6188ab7..e134065 100644
--- a/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/TimePickerActivity.kt
+++ b/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/TimePickerActivity.kt
@@ -16,6 +16,9 @@
 
 package androidx.wear.compose.material3.macrobenchmark.target
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.wear.compose.material3.macrobenchmark.common.TimePickerBenchmark
 
+@RequiresApi(Build.VERSION_CODES.O)
 class TimePickerActivity : BenchmarkBaseActivity(TimePickerBenchmark)
diff --git a/wear/compose/compose-material3/macrobenchmark/build.gradle b/wear/compose/compose-material3/macrobenchmark/build.gradle
index bcab91e..1940fef 100644
--- a/wear/compose/compose-material3/macrobenchmark/build.gradle
+++ b/wear/compose/compose-material3/macrobenchmark/build.gradle
@@ -23,7 +23,7 @@
 android {
     compileSdk = 35
     defaultConfig {
-        minSdk = 30
+        minSdk = 29
     }
     namespace = "androidx.wear.compose.material3.macrobenchmark"
     targetProjectPath = ":wear:compose:compose-material3-macrobenchmark-target"
diff --git a/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/DatePickerBenchmarkTest.kt b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/DatePickerBenchmarkTest.kt
index b9abc42..3316732 100644
--- a/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/DatePickerBenchmarkTest.kt
+++ b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/DatePickerBenchmarkTest.kt
@@ -16,12 +16,15 @@
 
 package androidx.wear.compose.material3.macrobenchmark
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.benchmark.macro.CompilationMode
 import androidx.test.filters.LargeTest
 import androidx.wear.compose.material3.macrobenchmark.common.DatePickerBenchmark
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
+@RequiresApi(Build.VERSION_CODES.O)
 @LargeTest
 @RunWith(Parameterized::class)
 class DatePickerBenchmarkTest(compilationMode: CompilationMode) :
diff --git a/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/TimePickerBenchmarkTest.kt b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/TimePickerBenchmarkTest.kt
index 1533377..52a5a38 100644
--- a/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/TimePickerBenchmarkTest.kt
+++ b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/TimePickerBenchmarkTest.kt
@@ -16,12 +16,15 @@
 
 package androidx.wear.compose.material3.macrobenchmark
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.benchmark.macro.CompilationMode
 import androidx.test.filters.LargeTest
 import androidx.wear.compose.material3.macrobenchmark.common.TimePickerBenchmark
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
+@RequiresApi(Build.VERSION_CODES.O)
 @LargeTest
 @RunWith(Parameterized::class)
 class TimePickerBenchmarkTest(compilationMode: CompilationMode) :
diff --git a/wear/compose/compose-material3/samples/build.gradle b/wear/compose/compose-material3/samples/build.gradle
index 814f3e9..02e43d8 100644
--- a/wear/compose/compose-material3/samples/build.gradle
+++ b/wear/compose/compose-material3/samples/build.gradle
@@ -50,7 +50,7 @@
 android {
     compileSdk = 35
     defaultConfig {
-        minSdk = 30
+        minSdk = 25
     }
     namespace = "androidx.wear.compose.material3.samples"
 }
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ButtonGroupSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ButtonGroupSample.kt
index a467fe1..9b6d145 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ButtonGroupSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ButtonGroupSample.kt
@@ -34,19 +34,58 @@
 @Sampled
 @Composable
 fun ButtonGroupSample() {
-    val interactionSourceLeft = remember { MutableInteractionSource() }
-    val interactionSourceRight = remember { MutableInteractionSource() }
+    val interactionSource1 = remember { MutableInteractionSource() }
+    val interactionSource2 = remember { MutableInteractionSource() }
+
     Box(Modifier.size(300.dp), contentAlignment = Alignment.Center) {
         ButtonGroup(Modifier.fillMaxWidth()) {
-            buttonGroupItem(interactionSource = interactionSourceLeft) {
-                Button(onClick = {}, interactionSource = interactionSourceLeft) {
-                    Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("L") }
-                }
+            Button(
+                onClick = {},
+                modifier = Modifier.enlargeOnPress(interactionSource1),
+                interactionSource = interactionSource1
+            ) {
+                Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("L") }
             }
-            buttonGroupItem(interactionSource = interactionSourceRight) {
-                Button(onClick = {}, interactionSource = interactionSourceRight) {
-                    Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("R") }
-                }
+            Button(
+                onClick = {},
+                modifier = Modifier.enlargeOnPress(interactionSource2),
+                interactionSource = interactionSource2
+            ) {
+                Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("R") }
+            }
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun ButtonGroupThreeButtonsSample() {
+    val interactionSource1 = remember { MutableInteractionSource() }
+    val interactionSource2 = remember { MutableInteractionSource() }
+    val interactionSource3 = remember { MutableInteractionSource() }
+
+    Box(Modifier.size(300.dp), contentAlignment = Alignment.Center) {
+        ButtonGroup(Modifier.fillMaxWidth()) {
+            Button(
+                onClick = {},
+                modifier = Modifier.enlargeOnPress(interactionSource1),
+                interactionSource = interactionSource1
+            ) {
+                Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("A") }
+            }
+            Button(
+                onClick = {},
+                modifier = Modifier.weight(1.5f).enlargeOnPress(interactionSource2),
+                interactionSource = interactionSource2
+            ) {
+                Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("B") }
+            }
+            Button(
+                onClick = {},
+                modifier = Modifier.enlargeOnPress(interactionSource3),
+                interactionSource = interactionSource3
+            ) {
+                Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("C") }
             }
         }
     }
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonGroupScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonGroupScreenshotTest.kt
index bfd690a..0b84d2b 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonGroupScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonGroupScreenshotTest.kt
@@ -18,8 +18,6 @@
 
 import android.os.Build
 import androidx.compose.foundation.background
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.runtime.remember
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -72,25 +70,25 @@
         require(numItems in 1..3)
         rule.setContentWithTheme {
             ScreenConfiguration(SCREEN_SIZE_SMALL) {
-                val interactionSource1 = remember { MutableInteractionSource() }
-                val interactionSource2 = remember { MutableInteractionSource() }
-                val interactionSource3 = remember { MutableInteractionSource() }
                 ButtonGroup(
                     Modifier.testTag(TEST_TAG),
                     spacing = spacing,
                     expansionWidth = expansionWidth
                 ) {
-                    buttonGroupItem(interactionSource1, minWidth1, weight1) {
-                        Text("A", Modifier.background(Color.Gray))
+                    // Modifiers inverted here to check order doesn't matter
+                    Text("A", Modifier.background(Color.Gray).weight(weight1).minWidth(minWidth1))
+                    if (numItems >= 2) {
+                        Text(
+                            "B",
+                            Modifier.background(Color.Gray).minWidth(minWidth2).weight(weight2)
+                        )
                     }
-                    if (numItems >= 2)
-                        buttonGroupItem(interactionSource2, minWidth2, weight2) {
-                            Text("B", Modifier.background(Color.Gray))
-                        }
-                    if (numItems >= 3)
-                        buttonGroupItem(interactionSource3, minWidth3, weight3) {
-                            Text("C", Modifier.background(Color.Gray))
-                        }
+                    if (numItems >= 3) {
+                        Text(
+                            "C",
+                            Modifier.background(Color.Gray).minWidth(minWidth3).weight(weight3)
+                        )
+                    }
                 }
             }
         }
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonGroupTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonGroupTest.kt
index b24182d..e28002d 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonGroupTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonGroupTest.kt
@@ -16,12 +16,10 @@
 
 package androidx.wear.compose.material3
 
-import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
-import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertWidthIsEqualTo
@@ -38,13 +36,10 @@
     @Test
     fun supports_testtag() {
         rule.setContentWithTheme {
-            val interactionSource = remember { MutableInteractionSource() }
             ButtonGroup(modifier = Modifier.testTag(TEST_TAG)) {
-                buttonGroupItem(interactionSource) {
-                    Box(
-                        modifier = Modifier.fillMaxSize(),
-                    )
-                }
+                Box(
+                    modifier = Modifier.fillMaxSize(),
+                )
             }
         }
 
@@ -53,76 +48,94 @@
 
     @Test
     fun two_items_equally_sized_by_default() =
-        doTest(
+        verifyWidths(
             2,
             expectedWidths = { availableSpace -> arrayOf(availableSpace / 2, availableSpace / 2) }
         )
 
     @Test
     fun two_items_one_double_size() =
-        doTest(
+        verifyWidths(
             2,
             expectedWidths = { availableSpace ->
                 arrayOf(availableSpace / 3, availableSpace / 3 * 2)
             },
-            minWidthsAndWeights = arrayOf(50.dp to 1f, 50.dp to 2f)
+            minWidthAndWeights = arrayOf(50.dp to 1f, 50.dp to 2f)
         )
 
     @Test
     fun respects_min_width() =
-        doTest(
+        verifyWidths(
             2,
             expectedWidths = { availableSpace -> arrayOf(30.dp, availableSpace - 30.dp) },
             size = 100.dp,
-            minWidthsAndWeights = arrayOf(30.dp to 1f, 30.dp to 10f)
+            minWidthAndWeights = arrayOf(30.dp to 1f, 30.dp to 10f)
         )
 
     @Test
     fun three_equal_buttons() =
-        doTest(3, expectedWidths = { availableSpace -> Array(3) { availableSpace / 3 } })
+        verifyWidths(3, expectedWidths = { availableSpace -> Array(3) { availableSpace / 3 } })
 
     @Test
     fun three_buttons_one_two_one() =
-        doTest(
+        verifyWidths(
             3,
             expectedWidths = { availableSpace ->
                 arrayOf(availableSpace / 4, availableSpace / 2, availableSpace / 4)
             },
-            minWidthsAndWeights = arrayOf(50.dp to 1f, 50.dp to 2f, 50.dp to 1f)
+            minWidthAndWeights = arrayOf(50.dp to 1f, 50.dp to 2f, 50.dp to 1f)
         )
 
-    private fun doTest(
+    @Test
+    fun modifier_order_ignored() {
+        val size = 300.dp
+        rule.setContentWithTheme {
+            ButtonGroup(
+                modifier = Modifier.size(size),
+                contentPadding = PaddingValues(0.dp),
+                spacing = 0.dp
+            ) {
+                Box(Modifier.weight(1f).minWidth(60.dp).testTag("${TEST_TAG}0"))
+                Box(Modifier.minWidth(60.dp).weight(1f).testTag("${TEST_TAG}1"))
+                Box(Modifier.weight(2f).minWidth(60.dp).testTag("${TEST_TAG}2"))
+                Box(Modifier.minWidth(60.dp).weight(2f).testTag("${TEST_TAG}3"))
+            }
+        }
+
+        // Items 0 & 1 should be 60.dp, 2 & 3 should be 90.dp
+        listOf(60.dp, 60.dp, 90.dp, 90.dp).forEachIndexed { index, dp ->
+            rule.onNodeWithTag(TEST_TAG + index.toString()).assertWidthIsEqualTo(dp)
+        }
+    }
+
+    private fun verifyWidths(
         numItems: Int,
         expectedWidths: (Dp) -> Array<Dp>,
         size: Dp = 300.dp,
         spacing: Dp = 10.dp,
-        minWidthsAndWeights: Array<Pair<Dp, Float>> = Array(numItems) { 48.dp to 1f },
+        minWidthAndWeights: Array<Pair<Dp, Float>> = Array(numItems) { 48.dp to 1f },
     ) {
         val horizontalPadding = 10.dp
         val actualExpectedWidths =
             expectedWidths(size - horizontalPadding * 2 - spacing * (numItems - 1))
 
         require(numItems == actualExpectedWidths.size)
-        require(numItems == minWidthsAndWeights.size)
+        require(numItems == minWidthAndWeights.size)
 
         rule.setContentWithTheme {
-            val interactionSources = remember { Array(numItems) { MutableInteractionSource() } }
             ButtonGroup(
                 modifier = Modifier.size(size),
                 contentPadding = PaddingValues(horizontal = horizontalPadding),
                 spacing = spacing
             ) {
                 repeat(numItems) { ix ->
-                    buttonGroupItem(
-                        interactionSources[ix],
-                        minWidth = minWidthsAndWeights[ix].first,
-                        weight = minWidthsAndWeights[ix].second
-                    ) {
-                        Box(
-                            modifier =
-                                Modifier.testTag(TEST_TAG + (ix + 1).toString()).fillMaxSize(),
-                        )
-                    }
+                    Box(
+                        modifier =
+                            Modifier.testTag(TEST_TAG + (ix + 1).toString())
+                                .fillMaxSize()
+                                .weight(minWidthAndWeights[ix].second)
+                                .minWidth(minWidthAndWeights[ix].first)
+                    )
                 }
             }
         }
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
index 722675e..a6c2594 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
@@ -277,6 +279,7 @@
         assertEquals(expectedSecondaryTextStyle, actualSecondaryLabelTextStyle)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun default_shape_is_stadium() {
         rule.isShape(
@@ -289,6 +292,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_shape_override() {
         val shape = CutCornerShape(4.dp)
@@ -376,6 +380,7 @@
             .assertTopPositionInRootIsEqualTo((itemBounds.height - iconBounds.height) / 2)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_base_button_correct_colors() {
         rule.verifyButtonColors(
@@ -385,6 +390,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_base_button_correct_colors() {
         rule.verifyButtonColors(
@@ -398,6 +404,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_filled_tonal_base_button_correct_colors() {
         rule.verifyButtonColors(
@@ -408,6 +415,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_filled_tonal_base_button_correct_colors() {
         rule.verifyButtonColors(
@@ -422,6 +430,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_button_correct_filled_variant_colors() {
         rule.verifyButtonColors(
@@ -432,6 +441,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_button_correct_filled_variant_colors() {
         rule.verifyButtonColors(
@@ -446,6 +456,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_outlined_base_button_correct_colors() {
         rule.verifyButtonColors(
@@ -456,6 +467,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_outlined_base_button_correct_colors() {
         rule.verifyButtonColors(
@@ -468,6 +480,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_child_base_button_correct_colors() {
         rule.verifyButtonColors(
@@ -478,6 +491,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_child_base_button_correct_colors() {
         rule.verifyButtonColors(
@@ -490,6 +504,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_three_slot_button_correct_colors() {
         rule.verifyThreeSlotButtonColors(
@@ -499,6 +514,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_three_slot_button_correct_colors() {
         rule.verifyThreeSlotButtonColors(
@@ -508,6 +524,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_filled_tonal_three_slot_button_correct_colors() {
         rule.verifyThreeSlotButtonColors(
@@ -517,6 +534,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_filled_tonal_three_slot_button_correct_colors() {
         rule.verifyThreeSlotButtonColors(
@@ -526,6 +544,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_outlined_three_slot_button_correct_colors() {
         rule.verifyThreeSlotButtonColors(
@@ -535,6 +554,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_outlined_three_slot_button_correct_colors() {
         rule.verifyThreeSlotButtonColors(
@@ -544,6 +564,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_child_three_slot_button_correct_colors() {
         rule.verifyThreeSlotButtonColors(
@@ -553,6 +574,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_child_three_slot_button_correct_colors() {
         rule.verifyThreeSlotButtonColors(
@@ -562,6 +584,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_outlined_button_correct_border_colors() {
         val status = Status.Enabled
@@ -573,6 +596,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_outlined_button_correct_border_colors() {
         val status = Status.Disabled
@@ -594,6 +618,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun overrides_enabled_outlined_button_border_color() {
         val status = Status.Enabled
@@ -615,6 +640,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun overrides_disabled_outlined_button_border_color() {
         val status = Status.Disabled
@@ -752,6 +778,7 @@
             )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_compact_button_correct_colors() {
         rule.verifyCompactButtonColors(
@@ -760,6 +787,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_compact_button_correct_colors() {
         rule.verifyCompactButtonColors(
@@ -768,6 +796,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_filled_tonal_compact_button_correct_colors() {
         rule.verifyCompactButtonColors(
@@ -776,6 +805,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_filled_tonal_compact_button_correct_colors() {
         rule.verifyCompactButtonColors(
@@ -784,6 +814,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_outlined_compact_button_correct_colors() {
         rule.verifyCompactButtonColors(
@@ -792,6 +823,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_outlined_compact_button_correct_colors() {
         rule.verifyCompactButtonColors(
@@ -800,6 +832,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_child_compact_button_correct_colors() {
         rule.verifyCompactButtonColors(
@@ -808,6 +841,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_child_compact_button_correct_colors() {
         rule.verifyCompactButtonColors(
@@ -1228,6 +1262,7 @@
     }
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun ComposeContentTestRule.verifyButtonColors(
     status: Status,
     expectedContainerColor: @Composable () -> Color,
@@ -1317,6 +1352,7 @@
     return actualContentColor
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun ComposeContentTestRule.verifyThreeSlotButtonColors(
     status: Status,
     expectedColor: @Composable () -> ButtonColors,
@@ -1419,6 +1455,7 @@
     return ThreeSlotButtonColors(actualLabelColor, actualSecondaryLabelColor, actualIconColor)
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 internal fun ComposeContentTestRule.verifyButtonBorderColor(
     expectedBorderColor: @Composable () -> Color,
     content: @Composable (Modifier) -> Unit
@@ -1436,6 +1473,7 @@
     onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(finalExpectedBorderColor)
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun ComposeContentTestRule.isShape(
     expectedShape: Shape,
     colors: @Composable () -> ButtonColors,
@@ -1469,6 +1507,7 @@
         )
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun ComposeContentTestRule.verifyCompactButtonColors(
     status: Status,
     colors: @Composable () -> ButtonColors
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CardTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CardTest.kt
index 62c88b3..98b8213 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CardTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CardTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
@@ -540,6 +542,7 @@
         assertEquals(expectedTitleColor, actualTitleColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun outlined_card_has_outlined_border_and_transparent() {
         val outlineColor = Color.Red
@@ -563,6 +566,7 @@
             .assertColorInPercentageRange(testBackground, 93f..97f)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun outlined_titlecard_has_outlined_border_and_transparent() {
         val outlineColor = Color.Red
@@ -588,6 +592,7 @@
             .assertColorInPercentageRange(testBackground, 93f..97f)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun outlined_appcard_has_outlined_border_and_transparent() {
         val outlineColor = Color.Red
@@ -691,6 +696,7 @@
         assertEquals(expectedContentTextStyle, actuaContentTextStyle)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun outlined_app_card_gives_correct_text_style_base() {
         var actualAppTextStyle = TextStyle.Default
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CheckboxButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CheckboxButtonTest.kt
index 0c34439..3b4f228 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CheckboxButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CheckboxButtonTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
@@ -549,6 +551,7 @@
         Assert.assertEquals(2, secondaryLabelMaxLines)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun checkbox_button_allows_checked_background_color_override() =
         verifyToggleButtonBackgroundColor(
@@ -557,6 +560,7 @@
             expectedColor = CHECKED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun checkbox_button_allows_unchecked_background_color_override() =
         verifyToggleButtonBackgroundColor(
@@ -565,6 +569,7 @@
             expectedColor = UNCHECKED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun split_checkbox_button_allows_checked_background_color_override() =
         verifySplitToggleButtonBackgroundColor(
@@ -573,6 +578,7 @@
             expectedColor = CHECKED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun split_checkbox_button_allows_unchecked_background_color_override() =
         verifySplitToggleButtonBackgroundColor(
@@ -581,46 +587,55 @@
             expectedColor = UNCHECKED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_checkbox_button_colors_enabled_and_checked() {
         rule.verifyCheckboxButtonColors(checked = true, enabled = true)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_checkbox_button_colors_enabled_and_unchecked() {
         rule.verifyCheckboxButtonColors(checked = false, enabled = true)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_checkbox_button_colors_disabled_and_checked() {
         rule.verifyCheckboxButtonColors(checked = true, enabled = false)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_checkbox_button_colors_disabled_and_unchecked() {
         rule.verifyCheckboxButtonColors(checked = false, enabled = false)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_split_checkbox_button_colors_enabled_and_checked() {
         rule.verifySplitCheckboxButtonColors(checked = true, enabled = true)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_split_checkbox_button_colors_enabled_and_unchecked() {
         rule.verifySplitCheckboxButtonColors(checked = false, enabled = true)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_split_checkbox_button_colors_disabled_and_checked() {
         rule.verifySplitCheckboxButtonColors(checked = true, enabled = false)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_split_checkbox_button_colors_disabled_and_unchecked() {
         rule.verifySplitCheckboxButtonColors(checked = false, enabled = false)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun checkbox_checked_colors_are_customisable() {
         val boxColor = Color.Green
@@ -643,6 +658,7 @@
         checkboxImage.assertContainsColor(checkmarkColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun checkbox_unchecked_colors_are_customisable() {
         // NB checkmark is erased during animation, so we don't test uncheckedCheckmarkColor
@@ -664,6 +680,7 @@
         checkboxImage.assertContainsColor(boxColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun disabled_checkbox_checked_colors_are_customisable() {
         val boxColor = Color.Green
@@ -686,6 +703,7 @@
         checkboxImage.assertContainsColor(checkmarkColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun disabled_checkbox_unchecked_colors_are_customisable() {
         // NB checkmark is erased during animation, so we don't test uncheckedCheckmarkColor
@@ -707,6 +725,7 @@
         checkboxImage.assertContainsColor(boxColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun verifyToggleButtonBackgroundColor(
         checked: Boolean,
         enabled: Boolean,
@@ -729,6 +748,7 @@
         rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun verifySplitToggleButtonBackgroundColor(
         checked: Boolean,
         enabled: Boolean,
@@ -797,6 +817,7 @@
         toggleContentDescription = "description",
     )
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun ComposeContentTestRule.verifyCheckboxButtonColors(enabled: Boolean, checked: Boolean) {
     val testBackgroundColor = Color.White
     var expectedContainerColor = Color.Transparent
@@ -836,6 +857,7 @@
         )
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun ComposeContentTestRule.verifySplitCheckboxButtonColors(
     enabled: Boolean,
     checked: Boolean
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CurvedTextTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CurvedTextTest.kt
index 2d16628..f42a497 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CurvedTextTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CurvedTextTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.testutils.assertContainsColor
 import androidx.compose.testutils.assertDoesNotContainColor
@@ -35,6 +37,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
+@RequiresApi(Build.VERSION_CODES.O)
 class CurvedTextTest {
     @get:Rule val rule = createComposeRule()
 
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/DatePickerScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/DatePickerScreenshotTest.kt
index fba9409..057cf19 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/DatePickerScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/DatePickerScreenshotTest.kt
@@ -17,6 +17,7 @@
 package androidx.wear.compose.material3
 
 import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.testutils.assertAgainstGolden
@@ -307,6 +308,7 @@
             )
             .onFirst()
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun ComposeContentTestRule.verifyDatePickerScreenshot(
         testName: TestName,
         screenshotRule: AndroidXScreenshotTestRule,
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
index 0d321cd..374863e 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
@@ -239,6 +241,7 @@
         rule.onNodeWithTag(TEST_TAG).assertIsOff().performClick().assertIsOff()
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun is_circular_under_ltr() =
         rule.isShape(
@@ -255,6 +258,7 @@
             )
         }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun is_circular_under_rtl() =
         rule.isShape(
@@ -271,6 +275,7 @@
             )
         }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_shape_overrides() =
         rule.isShape(
@@ -384,6 +389,7 @@
             )
         }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_checked_primary_colors() =
         rule.verifyIconToggleButtonColors(
@@ -394,6 +400,7 @@
             contentColor = { MaterialTheme.colorScheme.onPrimary }
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_unchecked_surface_colors() =
         rule.verifyIconToggleButtonColors(
@@ -404,6 +411,7 @@
             contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_unchecked_surface_colors_with_alpha() =
         rule.verifyIconToggleButtonColors(
@@ -416,6 +424,7 @@
             contentColor = { MaterialTheme.colorScheme.onSurface.toDisabledColor() }
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_primary_checked_contrasting_content_color() =
         rule.verifyIconToggleButtonColors(
@@ -428,6 +437,7 @@
             contentColor = { MaterialTheme.colorScheme.onSurface.toDisabledColor() },
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_checked_background_override() {
         val overrideColor = Color.Yellow
@@ -445,6 +455,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_checked_content_override() {
         val overrideColor = Color.Green
@@ -460,6 +471,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_unchecked_background_override() {
         val overrideColor = Color.Red
@@ -477,6 +489,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_unchecked_content_override() {
         val overrideColor = Color.Green
@@ -494,6 +507,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_checked_disabled_background_override() {
         val overrideColor = Color.Yellow
@@ -512,6 +526,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_checked_disabled_content_override() {
         val overrideColor = Color.Green
@@ -532,6 +547,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_unchecked_disabled_background_override() {
         val overrideColor = Color.Red
@@ -550,6 +566,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_unchecked_disabled_content_override() {
         val overrideColor = Color.Green
@@ -606,6 +623,7 @@
             .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, overrideRole))
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun animates_corners_to_75_percent_on_click() {
         val uncheckedShape = RoundedCornerShape(20.dp)
@@ -641,6 +659,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_unchecked_to_checked_shape_on_click() {
         val uncheckedShape = RoundedCornerShape(20.dp)
@@ -664,6 +683,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_checked_to_unchecked_shape_on_click() {
         val uncheckedShape = RoundedCornerShape(10.dp)
@@ -688,6 +708,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_to_unchecked_pressed_shape_when_pressed_on_unchecked() {
         val uncheckedShape = RoundedCornerShape(20.dp)
@@ -720,6 +741,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_to_checked_pressed_shape_when_pressed_on_checked() {
         val uncheckedShape = RoundedCornerShape(10.dp)
@@ -752,6 +774,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun ComposeContentTestRule.verifyIconToggleButtonColors(
         status: Status,
         checked: Boolean,
@@ -778,6 +801,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_unchecked_to_checked_shape_when_checked_changed() {
         val uncheckedShape = RoundedCornerShape(20.dp)
@@ -802,6 +826,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_checked_to_unchecked_shape_when_checked_changed() {
         val uncheckedShape = RoundedCornerShape(20.dp)
@@ -833,6 +858,7 @@
             .value
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun ComposeContentTestRule.isShape(
         shape: Shape = CircleShape,
         layoutDirection: LayoutDirection,
@@ -863,6 +889,7 @@
             )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun ComposeContentTestRule.verifyColors(
         expectedContainerColor: @Composable () -> Color,
         expectedContentColor: @Composable () -> Color,
@@ -884,6 +911,7 @@
         onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(finalExpectedContainerColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun ComposeContentTestRule.verifyCheckedStateChange(
         updateState: () -> Unit,
         startShape: Shape,
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/LevelIndicatorTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/LevelIndicatorTest.kt
index bda423d..4645acf 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/LevelIndicatorTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/LevelIndicatorTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
@@ -46,6 +48,7 @@
         rule.onNodeWithTag(TEST_TAG).assertExists()
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_indicator_correct_color() {
         var expectedColor: Color = Color.Unspecified
@@ -58,6 +61,7 @@
         rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_track_correct_color() {
         var expectedColor: Color = Color.Unspecified
@@ -70,6 +74,7 @@
         rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_indicator_custom_color() {
         val customColor = Color.Red
@@ -84,6 +89,7 @@
         rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_track_custom_color() {
         val customColor = Color.Red
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
index d990dc1a..d0333da 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
@@ -17,6 +17,8 @@
 package androidx.wear.compose.material3
 
 import android.content.res.Configuration
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
@@ -242,6 +244,7 @@
     onNodeWithTag(TEST_TAG).assertHeightIsEqualTo(expectedSize).assertWidthIsEqualTo(expectedSize)
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 internal fun ComposeContentTestRule.verifyColors(
     status: Status,
     expectedContainerColor: @Composable () -> Color,
@@ -376,6 +379,7 @@
     }
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 internal fun ComposeContentTestRule.verifyScreenshot(
     methodName: String,
     screenshotRule: AndroidXScreenshotTestRule,
@@ -396,6 +400,7 @@
     onNodeWithTag(testTag).captureToImage().assertAgainstGolden(screenshotRule, methodName)
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 fun ComposeContentTestRule.verifyRoundedButtonTapAnimationEnd(
     baseShape: RoundedCornerShape,
     pressedShape: RoundedCornerShape,
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PageIndicatorTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PageIndicatorTest.kt
index 84d54f5..203c279 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PageIndicatorTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PageIndicatorTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.testutils.assertContainsColor
 import androidx.compose.testutils.assertDoesNotContainColor
 import androidx.compose.ui.Modifier
@@ -33,6 +35,7 @@
 import org.junit.Rule
 import org.junit.Test
 
+@RequiresApi(Build.VERSION_CODES.O)
 class PageIndicatorTest {
     @get:Rule val rule = createComposeRule()
 
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PlaceholderTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PlaceholderTest.kt
index e98e268..3234616 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PlaceholderTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/PlaceholderTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
@@ -41,6 +43,7 @@
 import org.junit.Rule
 import org.junit.Test
 
+@RequiresApi(Build.VERSION_CODES.O)
 class PlaceholderTest {
     @get:Rule val rule = createComposeRule()
 
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/RadioButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/RadioButtonTest.kt
index 44d9c68..4b6a973 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/RadioButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/RadioButtonTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.RowScope
@@ -583,6 +585,7 @@
         Assert.assertEquals(2, secondaryLabelMaxLines)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun radio_button_allows_checked_background_color_override() =
         verifyRadioButtonBackgroundColor(
@@ -591,6 +594,7 @@
             expectedColor = SELECTED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun radio_button_allows_unchecked_background_color_override() =
         verifyRadioButtonBackgroundColor(
@@ -599,6 +603,7 @@
             expectedColor = UNSELECTED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun split_radio_button_allows_checked_background_color_override() =
         verifySplitRadioButtonBackgroundColor(
@@ -607,6 +612,7 @@
             expectedColor = SELECTED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun split_radio_button_allows_unchecked_background_color_override() =
         verifySplitRadioButtonBackgroundColor(
@@ -615,6 +621,7 @@
             expectedColor = UNSELECTED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun verifyRadioButtonBackgroundColor(
         selected: Boolean,
         enabled: Boolean,
@@ -637,6 +644,7 @@
         rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun verifySplitRadioButtonBackgroundColor(
         selected: Boolean,
         enabled: Boolean,
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt
index 9bcedf1..90674cb 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
@@ -59,6 +61,7 @@
 class ScrollAwayTest {
     @get:Rule val rule = createComposeRule()
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun showsTimeTextWithScalingLazyColumnInitially() {
         val timeTextColor = Color.Red
@@ -89,6 +92,7 @@
         rule.onNodeWithTag(TIME_TEXT_TAG).assertIsDisplayed()
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun showsTimeTextWithLazyColumnInitially() {
         val timeTextColor = Color.Red
@@ -101,6 +105,7 @@
         rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(timeTextColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun showsTimeTextWithColumnInitially() {
         val timeTextColor = Color.Red
@@ -113,6 +118,7 @@
         rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(timeTextColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun hidesTimeTextAfterScrollingScalingLazyColumn() {
         val timeTextColor = Color.Red
@@ -130,6 +136,7 @@
         rule.onNodeWithTag(TEST_TAG).captureToImage().assertDoesNotContainColor(timeTextColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun hidesTimeTextWithLazyColumn() {
         val timeTextColor = Color.Red
@@ -152,6 +159,7 @@
         rule.onNodeWithTag(TEST_TAG).captureToImage().assertDoesNotContainColor(timeTextColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun hidesTimeTextWithColumn() {
         val timeTextColor = Color.Red
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SwitchButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SwitchButtonTest.kt
index f9dd312..ec60445 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SwitchButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SwitchButtonTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
@@ -545,6 +547,7 @@
         Assert.assertEquals(2, secondaryLabelMaxLines)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun switch_button_allows_checked_background_color_override() =
         verifySwitchButtonBackgroundColor(
@@ -553,6 +556,7 @@
             expectedColor = CHECKED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun switch_button_allows_unchecked_background_color_override() =
         verifySwitchButtonBackgroundColor(
@@ -561,6 +565,7 @@
             expectedColor = UNCHECKED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun split_switch_button_allows_checked_background_color_override() =
         verifySplitSwitchButtonBackgroundColor(
@@ -569,6 +574,7 @@
             expectedColor = CHECKED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun split_switch_button_allows_unchecked_background_color_override() =
         verifySplitSwitchButtonBackgroundColor(
@@ -577,46 +583,55 @@
             expectedColor = UNCHECKED_COLOR
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_switch_button_colors_enabled_and_checked() {
         rule.verifySwitchButtonColors(checked = true, enabled = true)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_switch_button_colors_enabled_and_unchecked() {
         rule.verifySwitchButtonColors(checked = false, enabled = true)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_switch_button_colors_disabled_and_checked() {
         rule.verifySwitchButtonColors(checked = true, enabled = false)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_switch_button_colors_disabled_and_unchecked() {
         rule.verifySwitchButtonColors(checked = false, enabled = false)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_split_switch_button_colors_enabled_and_checked() {
         rule.verifySplitToggleButtonColors(checked = true, enabled = true)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_split_switch_button_colors_enabled_and_unchecked() {
         rule.verifySplitToggleButtonColors(checked = false, enabled = true)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_split_switch_button_colors_disabled_and_checked() {
         rule.verifySplitToggleButtonColors(checked = true, enabled = false)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun verify_split_toggle_button_colors_disabled_and_unchecked() {
         rule.verifySplitToggleButtonColors(checked = false, enabled = false)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun switch_checked_colors_are_customisable() {
         val thumbColor = Color.Green
@@ -645,6 +660,7 @@
         image.assertContainsColor(trackBorderColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun switch_unchecked_colors_are_customisable() {
         val thumbColor = Color.Green
@@ -672,6 +688,7 @@
         image.assertContainsColor(trackBorderColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun disabled_switch_checked_colors_are_customisable() {
         val thumbColor = Color.Green
@@ -700,6 +717,7 @@
         image.assertContainsColor(trackBorderColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun disabled_switch_unchecked_colors_are_customisable() {
         val thumbColor = Color.Green
@@ -724,6 +742,7 @@
         image.assertContainsColor(trackBorderColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun verifySwitchButtonBackgroundColor(
         checked: Boolean,
         enabled: Boolean,
@@ -746,6 +765,7 @@
         rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedColor)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun verifySplitSwitchButtonBackgroundColor(
         checked: Boolean,
         enabled: Boolean,
@@ -814,6 +834,7 @@
         toggleContentDescription = "description",
     )
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun ComposeContentTestRule.verifySwitchButtonColors(enabled: Boolean, checked: Boolean) {
     val testBackgroundColor = Color.White
     var expectedContainerColor = Color.Transparent
@@ -853,6 +874,7 @@
         )
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun ComposeContentTestRule.verifySplitToggleButtonColors(
     enabled: Boolean,
     checked: Boolean
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
index ddfc1b0..884643a 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.padding
@@ -335,6 +337,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun default_shape_is_circular() {
         rule.isShape(
@@ -347,6 +350,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_shape_override() {
         val shape = CutCornerShape(4.dp)
@@ -363,6 +367,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_text_button_colors() {
         rule.verifyTextButtonColors(
@@ -373,6 +378,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_text_button_colors() {
         rule.verifyTextButtonColors(
@@ -385,6 +391,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_filled_text_button_colors() {
         rule.verifyTextButtonColors(
@@ -395,6 +402,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_filled_text_button_colors() {
         rule.verifyTextButtonColors(
@@ -409,6 +417,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_filled_variant_text_button_colors() {
         rule.verifyTextButtonColors(
@@ -419,6 +428,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_filled_variant_text_button_colors() {
         rule.verifyTextButtonColors(
@@ -433,6 +443,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_filled_tonal_text_button_colors() {
         rule.verifyTextButtonColors(
@@ -443,6 +454,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_filled_tonal_text_button_colors() {
         rule.verifyTextButtonColors(
@@ -457,6 +469,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_outlined_text_button_colors() {
         rule.verifyTextButtonColors(
@@ -467,6 +480,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_outlined_text_button_colors() {
         rule.verifyTextButtonColors(
@@ -479,6 +493,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_enabled_outlined_text_button_correct_border_colors() {
         val status = Status.Enabled
@@ -496,6 +511,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_outlined_text_button_correct_border_colors() {
         val status = Status.Disabled
@@ -515,6 +531,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun overrides_outlined_text_button_border_color() {
         val status = Status.Enabled
@@ -536,6 +553,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun animates_corners_to_75_percent_on_click() {
         val baseShape = RoundedCornerShape(20.dp)
@@ -557,6 +575,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun ComposeContentTestRule.verifyTextButtonColors(
         status: Status,
         colors: @Composable () -> TextButtonColors,
@@ -584,6 +603,7 @@
     }
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun ComposeContentTestRule.isShape(
     expectedShape: Shape,
     colors: @Composable () -> TextButtonColors,
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
index 0d1b67e..fae1c050 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
@@ -241,6 +243,7 @@
         rule.onNodeWithTag(TEST_TAG).assertIsOff().performClick().assertIsOff()
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun is_circular_under_ltr() =
         rule.isShape(
@@ -257,6 +260,7 @@
             )
         }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun is_circular_under_rtl() =
         rule.isShape(
@@ -273,6 +277,7 @@
             )
         }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_shape_overrides() =
         rule.isShape(
@@ -374,6 +379,7 @@
             )
         }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_checked_primary_colors() =
         rule.verifyTextToggleButtonColors(
@@ -384,6 +390,7 @@
             contentColor = { MaterialTheme.colorScheme.onPrimary }
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_unchecked_surface_colors() =
         rule.verifyTextToggleButtonColors(
@@ -394,6 +401,7 @@
             contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_unchecked_surface_colors_with_alpha() =
         rule.verifyTextToggleButtonColors(
@@ -406,6 +414,7 @@
             contentColor = { MaterialTheme.colorScheme.onSurface.toDisabledColor() }
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun gives_disabled_primary_checked_contrasting_content_color() =
         rule.verifyTextToggleButtonColors(
@@ -418,6 +427,7 @@
             contentColor = { MaterialTheme.colorScheme.onSurface.toDisabledColor() },
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_checked_background_override() {
         val override = Color.Yellow
@@ -433,6 +443,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_checked_content_override() {
         val override = Color.Green
@@ -448,6 +459,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_unchecked_background_override() {
         val override = Color.Red
@@ -463,6 +475,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_unchecked_content_override() {
         val override = Color.Green
@@ -478,6 +491,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_checked_disabled_background_override() {
         val override = Color.Yellow
@@ -495,6 +509,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_checked_disabled_content_override() {
         val override = Color.Green
@@ -515,6 +530,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_unchecked_disabled_background_override() {
         val override = Color.Red
@@ -533,6 +549,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun allows_custom_unchecked_disabled_content_override() {
         val override = Color.Green
@@ -590,6 +607,7 @@
             .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, overrideRole))
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun animates_corners_to_75_percent_on_click() {
         val uncheckedShape = RoundedCornerShape(20.dp)
@@ -625,6 +643,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_unchecked_to_checked_shape_on_click() {
         val uncheckedShape = RoundedCornerShape(20.dp)
@@ -648,6 +667,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_checked_to_unchecked_shape_on_click() {
         val uncheckedShape = RoundedCornerShape(10.dp)
@@ -671,6 +691,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_to_unchecked_pressed_shape_when_pressed_on_unchecked() {
         val uncheckedShape = RoundedCornerShape(20.dp)
@@ -703,6 +724,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_to_checked_pressed_shape_when_pressed_on_checked() {
         val uncheckedShape = RoundedCornerShape(10.dp)
@@ -735,6 +757,7 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_unchecked_to_checked_shape_when_checked_changed() {
         val uncheckedShape = RoundedCornerShape(20.dp)
@@ -759,6 +782,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun changes_checked_to_unchecked_shape_when_checked_changed() {
         val uncheckedShape = RoundedCornerShape(20.dp)
@@ -790,6 +814,7 @@
             .value
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun ComposeContentTestRule.verifyTextToggleButtonColors(
         status: Status,
         checked: Boolean,
@@ -816,6 +841,7 @@
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun ComposeContentTestRule.isShape(
         shape: Shape = CircleShape,
         layoutDirection: LayoutDirection,
@@ -846,6 +872,7 @@
             )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun ComposeContentTestRule.verifyColors(
         expectedContainerColor: @Composable () -> Color,
         expectedContentColor: @Composable () -> Color,
@@ -879,6 +906,7 @@
             .assertWidthIsEqualTo(expectedSize)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun ComposeContentTestRule.verifyCheckedStateChange(
         updateState: () -> Unit,
         startShape: Shape,
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimePickerScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimePickerScreenshotTest.kt
index 1e80a24..3511aa3 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimePickerScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimePickerScreenshotTest.kt
@@ -17,6 +17,7 @@
 package androidx.wear.compose.material3
 
 import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.runtime.Composable
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
@@ -138,6 +139,7 @@
             }
         )
 
+    @RequiresApi(Build.VERSION_CODES.O)
     private fun ComposeContentTestRule.verifyTimePickerScreenshot(
         methodName: String,
         screenshotRule: AndroidXScreenshotTestRule,
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ButtonGroup.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ButtonGroup.kt
index 56b0729..ebfc393f 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ButtonGroup.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ButtonGroup.kt
@@ -16,67 +16,53 @@
 
 package androidx.wear.compose.material3
 
+import androidx.annotation.FloatRange
 import androidx.annotation.VisibleForTesting
 import androidx.compose.animation.core.Animatable
-import androidx.compose.foundation.interaction.InteractionSource
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.interaction.PressInteraction
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ParentDataModifierNode
+import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.unit.takeOrElse
 import androidx.compose.ui.util.fastForEachIndexed
+import androidx.compose.ui.util.fastIsFinite
 import androidx.compose.ui.util.fastMap
 import androidx.compose.ui.util.fastMapIndexed
 import androidx.wear.compose.materialcore.screenHeightDp
 import kotlin.math.abs
 import kotlin.math.roundToInt
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.launch
 
-/** Scope for the children of a [ButtonGroup] */
-public class ButtonGroupScope {
-    internal val items = mutableListOf<ButtonGroupItem>()
-
-    /**
-     * Adds an item to a [ButtonGroup]
-     *
-     * @param interactionSource the interactionSource used to detect press/release events. Should be
-     *   the same one used in the content in this slot, which is typically a [Button].
-     * @param minWidth the minimum width this item can be. This will only be used if distributing
-     *   the available space results on a item falling below it's minimum width.
-     * @param weight the main way of distributing available space. In most cases, items will have a
-     *   width assigned proportional to their width (and available space). The exception is if that
-     *   will make some item(s) width fall below it's minWidth.
-     * @param content the content to use for this item. Usually, this will be one of the [Button]
-     *   variants.
-     */
-    public fun buttonGroupItem(
-        interactionSource: InteractionSource,
-        minWidth: Dp = minimumInteractiveComponentSize,
-        weight: Float = 1f,
-        content: @Composable () -> Unit
-    ): Boolean = items.add(ButtonGroupItem(interactionSource, minWidth, weight, content))
-}
-
 /**
- * Layout component to implement an expressive group of buttons, that react to touch by growing the
- * touched button, (while the neighbor(s) shrink to accommodate and keep the group width constant).
+ * Layout component to implement an expressive group of buttons in a row, that react to touch by
+ * growing the touched button, (while the neighbor(s) shrink to accommodate and keep the group width
+ * constant).
  *
  * Example of a [ButtonGroup]:
  *
  * @sample androidx.wear.compose.material3.samples.ButtonGroupSample
+ *
+ * Example of 3 buttons, the middle one bigger [ButtonGroup]:
+ *
+ * @sample androidx.wear.compose.material3.samples.ButtonGroupThreeButtonsSample
  * @param modifier Modifier to be applied to the button group
  * @param spacing the amount of spacing between buttons
  * @param expansionWidth how much buttons grow when pressed
@@ -84,7 +70,8 @@
  *   content
  * @param verticalAlignment the vertical alignment of the button group's children.
  * @param content the content and properties of each button. The Ux guidance is to use no more than
- *   3 buttons within a ButtonGroup.
+ *   3 buttons within a ButtonGroup. Note that this content is on the [ButtonGroupScope], to provide
+ *   access to 3 new modifiers to configure the buttons.
  */
 @Composable
 public fun ButtonGroup(
@@ -93,98 +80,77 @@
     expansionWidth: Dp = ButtonGroupDefaults.ExpansionWidth,
     contentPadding: PaddingValues = ButtonGroupDefaults.fullWidthPaddings(),
     verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
-    content: ButtonGroupScope.() -> Unit
+    content: @Composable ButtonGroupScope.() -> Unit
 ) {
-    val actualContent = ButtonGroupScope().apply(block = content)
-
-    val pressedStates = remember { Array(actualContent.items.size) { mutableStateOf(false) } }
-
-    val animatedSizes = remember { Array(actualContent.items.size) { Animatable(0f) } }
-
     val expandAmountPx = with(LocalDensity.current) { expansionWidth.toPx() }
 
     val downAnimSpec = MaterialTheme.motionScheme.fastSpatialSpec<Float>().faster(100f)
     val upAnimSpec = MaterialTheme.motionScheme.slowSpatialSpec<Float>()
 
-    LaunchedEffect(actualContent.items) {
-        launch {
-            val pressInteractions =
-                Array(actualContent.items.size) { mutableListOf<PressInteraction.Press>() }
-
-            merge(
-                    flows =
-                        Array(actualContent.items.size) { index ->
-                            // Annotate each flow with the item index it is related to.
-                            actualContent.items[index].interactionSource.interactions.map {
-                                interaction ->
-                                index to interaction
-                            }
-                        }
-                )
-                .collect { (index, interaction) ->
-                    when (interaction) {
-                        is PressInteraction.Press -> pressInteractions[index].add(interaction)
-                        is PressInteraction.Release ->
-                            pressInteractions[index].remove(interaction.press)
-                        is PressInteraction.Cancel ->
-                            pressInteractions[index].remove(interaction.press)
-                    }
-                    pressedStates[index].value = pressInteractions[index].isNotEmpty()
+    val scope = remember {
+        object : ButtonGroupScope {
+            override fun Modifier.weight(weight: Float): Modifier {
+                require(weight >= 0.0) {
+                    "invalid weight $weight; must be greater or equal to zero"
                 }
-        }
-
-        actualContent.items.indices.forEach { index ->
-            launch {
-                snapshotFlow { pressedStates[index].value }
-                    .collectLatest { value ->
-                        if (value) {
-                            animatedSizes[index].animateTo(expandAmountPx, downAnimSpec)
-                        } else {
-                            animatedSizes[index].animateTo(0f, upAnimSpec)
-                        }
-                    }
+                return this.then(ButtonGroupElement(weight = weight))
             }
+
+            override fun Modifier.minWidth(minWidth: Dp): Modifier {
+                require(minWidth > 0.dp) { "invalid minWidth $minWidth; must be greater than zero" }
+                return this.then(ButtonGroupElement(minWidth = minWidth))
+            }
+
+            override fun Modifier.enlargeOnPress(interactionSource: MutableInteractionSource) =
+                this.then(
+                    EnlargeOnPressElement(
+                        interactionSource = interactionSource,
+                        downAnimSpec,
+                        upAnimSpec
+                    )
+                )
         }
     }
 
-    Layout(
-        modifier = modifier.padding(contentPadding),
-        content = { actualContent.items.fastForEach { it.content() } }
-    ) { measurables, constraints ->
+    Layout(modifier = modifier.padding(contentPadding), content = { scope.content() }) {
+        measurables,
+        constraints ->
         require(constraints.hasBoundedWidth) { "ButtonGroup width cannot be unbounded." }
-        require(measurables.size == actualContent.items.size) {
-            "ButtonGroup's items have to produce exactly one composable each."
-        }
 
         val width = constraints.maxWidth
         val spacingPx = spacing.roundToPx()
 
+        val configs =
+            Array(measurables.size) {
+                measurables[it].parentData as? ButtonGroupParentData
+                    ?: ButtonGroupParentData.DEFAULT
+            }
+
+        val animatedSizes = Array(measurables.size) { configs[it].pressedState.value }
+
         // TODO: Cache this if it proves to be computationally intensive.
         val widths =
-            computeWidths(
-                actualContent.items.fastMap { it.minWidth.toPx() to it.weight },
-                spacingPx,
-                width
-            )
+            computeWidths(configs.map { it.minWidth.toPx() to it.weight }, spacingPx, width)
 
         // Add animated grow/shrink
-        if (actualContent.items.size > 1) {
-            animatedSizes.forEachIndexed { index, value ->
+        if (measurables.size > 1) {
+            for (index in measurables.indices) {
+                val value = animatedSizes[index] * expandAmountPx
                 // How much we need to grow the pressed item.
                 val growth: Int
-                if (index in 1 until animatedSizes.lastIndex) {
-                    // index is in the middle. Ensure we keep the size of the middle element with
+                if (index in 1 until measurables.lastIndex) {
+                    // index is in the middle. Ensure we keep the size of the middle item with
                     // the same parity, so its content remains in place.
-                    growth = (value.value / 2).roundToInt() * 2
+                    growth = (value / 2).roundToInt() * 2
                     widths[index - 1] -= growth / 2
                     widths[index + 1] -= growth / 2
                 } else {
-                    growth = value.value.roundToInt()
+                    growth = value.roundToInt()
                     if (index == 0) {
                         // index == 0, and we know there are at least 2 items.
                         widths[1] -= growth
                     } else {
-                        // index == animatedSizes.lastIndex, and we know there are at least 2 items.
+                        // index == measurables.lastIndex, and we know there are at least 2 items.
                         widths[index - 1] -= growth
                     }
                 }
@@ -218,6 +184,37 @@
     }
 }
 
+public interface ButtonGroupScope {
+    /**
+     * [ButtonGroup] uses a ratio of all sibling item [weight]s to assign a width to each item. The
+     * horizontal space is distributed using [weight] first, and this will only be changed if any
+     * item would be smaller than its [minWidth]. See also [Modifier.minWidth].
+     *
+     * @param weight The main way of distributing available space. This is a relative measure, and
+     *   items with no weight specified will have a default of 1f.
+     */
+    public fun Modifier.weight(
+        @FloatRange(from = 0.0, fromInclusive = false) weight: Float
+    ): Modifier
+
+    /**
+     * Specifies the minimum width this item can be, in Dp. This will only be used if distributing
+     * the available space results in a item falling below its minimum width. Note that this is only
+     * used before animations, pressing a button may result on neighbor button(s) going below their
+     * minWidth. See also [Modifier.weight]
+     *
+     * @param minWidth the minimum width. If none is specified, minimumInteractiveComponentSize is
+     *   used.
+     */
+    public fun Modifier.minWidth(minWidth: Dp = minimumInteractiveComponentSize): Modifier
+
+    /**
+     * Specifies the interaction source to use with this item. This is used to listen to events and
+     * grow/shrink the buttons in reaction.
+     */
+    public fun Modifier.enlargeOnPress(interactionSource: MutableInteractionSource): Modifier
+}
+
 /** Contains the default values used by [ButtonGroup] */
 public object ButtonGroupDefaults {
     /**
@@ -246,22 +243,160 @@
 /**
  * Data class to configure one item in a [ButtonGroup]
  *
- * @param interactionSource the interactionSource used to detect press/release events. Should be the
- *   same one used in the content in this slot, which is typically a [Button].
- * @param minWidth the minimum width this item can be. This will only be used if distributing the
- *   available space results on a item falling below it's minimum width.
  * @param weight the main way of distributing available space. In most cases, items will have a
- *   width assigned proportional to their width (and available space). The exception is if that will
- *   make some item(s) width fall below it's minWidth.
- * @param content the content to use for this item. Usually, this will be one of the [Button]
- *   variants.
+ *   width assigned proportional to their weight (and available space). The exception is if that
+ *   will make some item(s) width fall below its minWidth.
+ * @param minWidth the minimum width this item can be. This will only be used if distributing the
+ *   available space results on a item falling below its minimum width.
+ * @param pressedState an animated float between 0f and 1f that captures an animated, continuous
+ *   version of the item's interaction source pressed state.
  */
-internal data class ButtonGroupItem(
-    val interactionSource: InteractionSource,
-    val minWidth: Dp = minimumInteractiveComponentSize,
-    val weight: Float = 1f,
-    val content: @Composable () -> Unit
-)
+internal data class ButtonGroupParentData(
+    val weight: Float,
+    val minWidth: Dp,
+    val pressedState: Animatable<Float, AnimationVector1D>,
+) {
+    companion object {
+        val DEFAULT = ButtonGroupParentData(1f, minimumInteractiveComponentSize, Animatable(0f))
+    }
+}
+
+internal class ButtonGroupElement(
+    val weight: Float = Float.NaN,
+    val minWidth: Dp = Dp.Unspecified,
+) : ModifierNodeElement<ButtonGroupNode>() {
+
+    override fun create(): ButtonGroupNode {
+        return ButtonGroupNode(weight, minWidth)
+    }
+
+    override fun update(node: ButtonGroupNode) {
+        node.weight = weight
+        node.minWidth = minWidth
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "ButtonGroupElement"
+        properties["weight"] = weight
+        properties["minWidth"] = minWidth
+    }
+
+    override fun hashCode() = weight.hashCode() * 31 + minWidth.hashCode()
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        val otherModifier = other as? ButtonGroupNode ?: return false
+        return weight == otherModifier.weight && minWidth == otherModifier.minWidth
+    }
+}
+
+internal class ButtonGroupNode(var weight: Float, var minWidth: Dp) :
+    ParentDataModifierNode, Modifier.Node() {
+    override fun Density.modifyParentData(parentData: Any?) =
+        (parentData as? ButtonGroupParentData ?: ButtonGroupParentData.DEFAULT).let { prev ->
+            ButtonGroupParentData(
+                if (weight.fastIsFinite()) weight else prev.weight,
+                minWidth.takeOrElse { prev.minWidth },
+                prev.pressedState
+            )
+        }
+}
+
+internal class EnlargeOnPressElement(
+    val interactionSource: MutableInteractionSource,
+    val downAnimSpec: AnimationSpec<Float>,
+    val upAnimSpec: AnimationSpec<Float>,
+) : ModifierNodeElement<EnlargeOnPressNode>() {
+
+    override fun create(): EnlargeOnPressNode {
+        return EnlargeOnPressNode(interactionSource, downAnimSpec, upAnimSpec)
+    }
+
+    override fun update(node: EnlargeOnPressNode) {
+        if (node.interactionSource != interactionSource) {
+            node.interactionSource = interactionSource
+            node.launchCollectionJob()
+        }
+        node.downAnimSpec = downAnimSpec
+        node.upAnimSpec = upAnimSpec
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "EnlargeOnPressElement"
+        properties["interactionSource"] = interactionSource
+        properties["downAnimSpec"] = downAnimSpec
+        properties["upAnimSpec"] = upAnimSpec
+    }
+
+    override fun hashCode() =
+        (interactionSource.hashCode() * 31 + downAnimSpec.hashCode()) * 31 + upAnimSpec.hashCode()
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        val otherModifier = other as? EnlargeOnPressNode ?: return false
+        return interactionSource == otherModifier.interactionSource &&
+            downAnimSpec == otherModifier.downAnimSpec &&
+            upAnimSpec == otherModifier.upAnimSpec
+    }
+}
+
+internal class EnlargeOnPressNode(
+    var interactionSource: MutableInteractionSource,
+    var downAnimSpec: AnimationSpec<Float>,
+    var upAnimSpec: AnimationSpec<Float>,
+) : ParentDataModifierNode, Modifier.Node() {
+    private val pressedAnimatable: Animatable<Float, AnimationVector1D> = Animatable(0f)
+
+    private var collectionJob: Job? = null
+
+    override fun onAttach() {
+        super.onAttach()
+
+        launchCollectionJob()
+    }
+
+    override fun onDetach() {
+        super.onDetach()
+        collectionJob = null
+    }
+
+    internal fun launchCollectionJob() {
+        collectionJob?.cancel()
+        collectionJob =
+            coroutineScope.launch {
+                val pressInteractions = mutableListOf<PressInteraction.Press>()
+
+                launch {
+                    // Use collect here to ensure we don't lose any events.
+                    interactionSource.interactions
+                        .map { interaction ->
+                            when (interaction) {
+                                is PressInteraction.Press -> pressInteractions.add(interaction)
+                                is PressInteraction.Release ->
+                                    pressInteractions.remove(interaction.press)
+                                is PressInteraction.Cancel ->
+                                    pressInteractions.remove(interaction.press)
+                            }
+                            pressInteractions.isNotEmpty()
+                        }
+                        .distinctUntilChanged()
+                        .collectLatest { pressed ->
+                            if (pressed) {
+                                launch { pressedAnimatable.animateTo(1f, downAnimSpec) }
+                            } else {
+                                waitUntil { pressedAnimatable.value > 0.75f }
+                                pressedAnimatable.animateTo(0f, upAnimSpec)
+                            }
+                        }
+                }
+            }
+    }
+
+    override fun Density.modifyParentData(parentData: Any?) =
+        (parentData as? ButtonGroupParentData ?: ButtonGroupParentData.DEFAULT).let { prev ->
+            ButtonGroupParentData(prev.weight, prev.minWidth, pressedAnimatable)
+        }
+}
 
 // TODO: Does it make sense to unify these 2 classes?
 private data class ComputeHelper(
@@ -295,8 +430,8 @@
     val totalWeight = helper.map { it.weight }.sum()
 
     val extraSpace = availableWidth - minSpaceNeeded
-    // TODO: should we really handle the totalWeight <= 0 case? If so, we need to leave items at
-    //  their minWidth and center the whole thing?
+    // TODO: should we really handle the totalWeight <= 0 case? If so, we need to leave items
+    // at their minWidth and center the whole thing?
     if (totalWeight > 0) {
         for (ix in helper.indices) {
             // Initial distribution ignores minWidth.
@@ -312,9 +447,11 @@
     // Sort them. We will have:
     // * Items with weight == 0 and less width required (usually 0)
     // * Items with weight > 0 and less width required
-    // * Items with weight > 0, sorted for the order in which they may get below their minimum width
+    // * Items with weight > 0, sorted for the order in which they may get below their
+    // minimum width
     //   as we take away space.
-    // * Items with weight == 0 and enough width (This can only happen if totalWeight == 0)
+    // * Items with weight == 0 and enough width (This can only happen if totalWeight
+    // == 0)
     helper.sortBy {
         if (it.weight == 0f) {
             if (it.width < it.minWidth) Float.MIN_VALUE else Float.MAX_VALUE
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/DatePicker.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/DatePicker.kt
index 2879d30..6d981e3 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/DatePicker.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/DatePicker.kt
@@ -16,7 +16,9 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
 import android.text.format.DateFormat
+import androidx.annotation.RequiresApi
 import androidx.compose.animation.core.Animatable
 import androidx.compose.foundation.focusable
 import androidx.compose.foundation.layout.Arrangement
@@ -96,6 +98,7 @@
  * @param datePickerType The different [DatePickerType] supported by this [DatePicker].
  * @param colors [DatePickerColors] to be applied to the DatePicker.
  */
+@RequiresApi(Build.VERSION_CODES.O)
 @Composable
 public fun DatePicker(
     initialDate: LocalDate,
@@ -696,6 +699,7 @@
         else -> arrayOf(DatePickerOption.Day, DatePickerOption.Month, DatePickerOption.Year)
     }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun verifyDates(
     date: LocalDate,
     minDate: LocalDate,
@@ -705,6 +709,7 @@
     require(date in minDate..maxDate) { "date should lie between minDate and maxDate" }
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun getMonthNames(pattern: String): List<String> {
     val monthFormatter = DateTimeFormatter.ofPattern(pattern)
     val months = 1..12
@@ -730,6 +735,7 @@
     }
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private class DatePickerState(
     initialDate: LocalDate,
     initialDateMinYear: LocalDate?,
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/PickerGroup.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/PickerGroup.kt
index ece1094..78277fc 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/PickerGroup.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/PickerGroup.kt
@@ -24,6 +24,7 @@
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
index 92016f0..13726ac 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
@@ -65,7 +65,6 @@
     content: @Composable BoxScope.() -> Unit,
 ) {
     val borderStroke = border(enabled)
-
     Box(
         contentAlignment = Alignment.Center,
         modifier =
@@ -158,11 +157,10 @@
                 onPressAnimationSpec = onPressAnimationSpec,
                 onReleaseAnimationSpec = onReleaseAnimationSpec
             )
-
-        Pair(finalShape, finalInteractionSource)
+        finalShape to finalInteractionSource
     } else {
         // Fallback to static uncheckedShape if no other shapes, or not animatable
-        Pair(defaultShape, interactionSource)
+        defaultShape to interactionSource
     }
 
 @Composable
@@ -195,22 +193,19 @@
 
         val finalInteractionSource = interactionSource ?: remember { MutableInteractionSource() }
 
-        val finalShape =
-            rememberAnimatedToggleRoundedCornerShape(
-                interactionSource = finalInteractionSource,
-                uncheckedCornerSize = uncheckedShape.topEnd,
-                checkedCornerSize = checkedShape.topEnd,
-                uncheckedPressedCornerSize = uncheckedPressedShape.topEnd,
-                checkedPressedCornerSize = checkedPressedShape.topEnd,
-                checked = checked,
-                onPressAnimationSpec = onPressAnimationSpec,
-                onReleaseAnimationSpec = onReleaseAnimationSpec,
-            )
-
-        Pair(finalShape, finalInteractionSource)
+        rememberAnimatedToggleRoundedCornerShape(
+            interactionSource = finalInteractionSource,
+            uncheckedCornerSize = uncheckedShape.topEnd,
+            checkedCornerSize = checkedShape.topEnd,
+            uncheckedPressedCornerSize = uncheckedPressedShape.topEnd,
+            checkedPressedCornerSize = checkedPressedShape.topEnd,
+            checked = checked,
+            onPressAnimationSpec = onPressAnimationSpec,
+            onReleaseAnimationSpec = onReleaseAnimationSpec,
+        ) to finalInteractionSource
     } else {
         // Fallback to static uncheckedShape if no other shapes, or not animatable
-        Pair(uncheckedShape, interactionSource)
+        uncheckedShape to interactionSource
     }
 }
 
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextToggleButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextToggleButton.kt
index 2ae6941..acd3223 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextToggleButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextToggleButton.kt
@@ -39,6 +39,7 @@
 import androidx.wear.compose.material3.tokens.MotionTokens
 import androidx.wear.compose.material3.tokens.ShapeTokens
 import androidx.wear.compose.material3.tokens.TextToggleButtonTokens
+import androidx.wear.compose.materialcore.ToggleButton
 import androidx.wear.compose.materialcore.animateSelectionColor
 
 /**
@@ -110,7 +111,7 @@
             interactionSource = interactionSource
         )
 
-    androidx.wear.compose.materialcore.ToggleButton(
+    ToggleButton(
         checked = checked,
         onCheckedChange = onCheckedChange,
         modifier = modifier.minimumInteractiveComponentSize(),
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimePicker.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimePicker.kt
index 01b7923..4f2571d 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimePicker.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimePicker.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.compose.animation.core.Animatable
 import androidx.compose.foundation.focusable
 import androidx.compose.foundation.layout.Arrangement
@@ -95,6 +97,7 @@
  *   whether to show seconds or AM/PM selector as well as hours and minutes.
  * @param colors [TimePickerColors] be applied to the TimePicker.
  */
+@RequiresApi(Build.VERSION_CODES.O)
 @Composable
 public fun TimePicker(
     initialTime: LocalTime,
@@ -593,6 +596,7 @@
 }
 
 /* Returns the picker data for the third column (AM/PM or seconds) based on the time picker type. */
+@RequiresApi(Build.VERSION_CODES.O)
 @Composable
 private fun getOptionalThirdPicker(
     timePickerType: TimePickerType,
diff --git a/wear/compose/integration-tests/demos/build.gradle b/wear/compose/integration-tests/demos/build.gradle
index 3298f37..8426c60 100644
--- a/wear/compose/integration-tests/demos/build.gradle
+++ b/wear/compose/integration-tests/demos/build.gradle
@@ -25,7 +25,7 @@
     compileSdk = 35
     defaultConfig {
         applicationId = "androidx.wear.compose.integration.demos"
-        minSdk = 30
+        minSdk = 25
         versionCode = 63
         versionName = "1.63"
     }
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoActivity.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoActivity.kt
index 883f184..938f23f 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoActivity.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoActivity.kt
@@ -19,11 +19,13 @@
 import android.app.Activity
 import android.content.Context
 import android.content.Intent
+import android.os.Build
 import android.os.Bundle
 import android.view.View
 import androidx.activity.ComponentActivity
 import androidx.activity.OnBackPressedCallback
 import androidx.activity.OnBackPressedDispatcher
+import androidx.annotation.RequiresApi
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
@@ -53,6 +55,7 @@
     lateinit var hostView: View
     lateinit var focusManager: FocusManager
 
+    @RequiresApi(Build.VERSION_CODES.O)
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
index 169ba5bc..1aa5314 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
@@ -16,6 +16,7 @@
 
 package androidx.wear.compose.integration.demos
 
+import android.os.Build
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -146,65 +147,71 @@
             ),
             DemoCategory(
                 "Picker",
-                listOf(
-                    ComposableDemo("Time HH:MM:SS") { params ->
-                        var timePickerTime by remember { mutableStateOf(LocalTime.now()) }
-                        TimePicker(
-                            onTimeConfirm = {
-                                timePickerTime = it
-                                params.navigateBack()
-                            },
-                            time = timePickerTime,
-                        )
-                    },
-                    ComposableDemo("Time 12 Hour") { params ->
-                        var timePickerTime by remember { mutableStateOf(LocalTime.now()) }
-                        TimePickerWith12HourClock(
-                            onTimeConfirm = {
-                                timePickerTime = it
-                                params.navigateBack()
-                            },
-                            time = timePickerTime,
-                        )
-                    },
-                    ComposableDemo("Date Picker") { params ->
-                        var datePickerDate by remember { mutableStateOf(LocalDate.now()) }
-                        DatePicker(
-                            onDateConfirm = {
-                                datePickerDate = it
-                                params.navigateBack()
-                            },
-                            date = datePickerDate
-                        )
-                    },
-                    ComposableDemo("From Date Picker") { params ->
-                        var datePickerDate by remember { mutableStateOf(LocalDate.now()) }
-                        DatePicker(
-                            onDateConfirm = {
-                                datePickerDate = it
-                                params.navigateBack()
-                            },
-                            date = datePickerDate,
-                            fromDate = datePickerDate
-                        )
-                    },
-                    ComposableDemo("To Date Picker") { params ->
-                        var datePickerDate by remember { mutableStateOf(LocalDate.now()) }
-                        DatePicker(
-                            onDateConfirm = {
-                                datePickerDate = it
-                                params.navigateBack()
-                            },
-                            date = datePickerDate,
-                            toDate = datePickerDate
-                        )
-                    },
-                    ComposableDemo("Simple Picker") { SimplePicker() },
-                    ComposableDemo("No gradient") { PickerWithoutGradient() },
-                    ComposableDemo("Animate picker change") { AnimateOptionChangePicker() },
-                    ComposableDemo("Sample Picker Group") { PickerGroup24Hours() },
-                    ComposableDemo("Autocentering Picker Group") { AutoCenteringPickerGroup() }
-                )
+                if (Build.VERSION.SDK_INT > 25) {
+                    listOf(
+                        ComposableDemo("Time HH:MM:SS") { params ->
+                            var timePickerTime by remember { mutableStateOf(LocalTime.now()) }
+                            TimePicker(
+                                onTimeConfirm = {
+                                    timePickerTime = it
+                                    params.navigateBack()
+                                },
+                                time = timePickerTime,
+                            )
+                        },
+                        ComposableDemo("Time 12 Hour") { params ->
+                            var timePickerTime by remember { mutableStateOf(LocalTime.now()) }
+                            TimePickerWith12HourClock(
+                                onTimeConfirm = {
+                                    timePickerTime = it
+                                    params.navigateBack()
+                                },
+                                time = timePickerTime,
+                            )
+                        },
+                        ComposableDemo("Date Picker") { params ->
+                            var datePickerDate by remember { mutableStateOf(LocalDate.now()) }
+                            DatePicker(
+                                onDateConfirm = {
+                                    datePickerDate = it
+                                    params.navigateBack()
+                                },
+                                date = datePickerDate
+                            )
+                        },
+                        ComposableDemo("From Date Picker") { params ->
+                            var datePickerDate by remember { mutableStateOf(LocalDate.now()) }
+                            DatePicker(
+                                onDateConfirm = {
+                                    datePickerDate = it
+                                    params.navigateBack()
+                                },
+                                date = datePickerDate,
+                                fromDate = datePickerDate
+                            )
+                        },
+                        ComposableDemo("To Date Picker") { params ->
+                            var datePickerDate by remember { mutableStateOf(LocalDate.now()) }
+                            DatePicker(
+                                onDateConfirm = {
+                                    datePickerDate = it
+                                    params.navigateBack()
+                                },
+                                date = datePickerDate,
+                                toDate = datePickerDate
+                            )
+                        },
+                        ComposableDemo("Simple Picker") { SimplePicker() },
+                        ComposableDemo("No gradient") { PickerWithoutGradient() },
+                        ComposableDemo("Animate picker change") { AnimateOptionChangePicker() },
+                        ComposableDemo("Sample Picker Group") { PickerGroup24Hours() },
+                        ComposableDemo("Autocentering Picker Group") { AutoCenteringPickerGroup() }
+                    )
+                } else {
+                    listOf(
+                        ComposableDemo("Simple Picker") { SimplePicker() },
+                    )
+                }
             ),
             DemoCategory(
                 "Slider",
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt
index 310a4b7..223bcbe 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt
@@ -17,9 +17,11 @@
 package androidx.wear.compose.integration.demos
 
 import android.content.Context
+import android.os.Build
 import android.view.accessibility.AccessibilityManager
 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener
 import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
+import androidx.annotation.RequiresApi
 import androidx.compose.animation.core.Animatable
 import androidx.compose.foundation.focusable
 import androidx.compose.foundation.layout.Arrangement
@@ -93,6 +95,7 @@
  * @param modifier the modifiers for the `Box` containing the UI elements.
  * @param time the initial value to seed the picker with.
  */
+@RequiresApi(Build.VERSION_CODES.O)
 @Composable
 public fun TimePicker(
     onTimeConfirm: (LocalTime) -> Unit,
@@ -265,6 +268,7 @@
  * @param modifier the modifiers for the `Column` containing the UI elements.
  * @param time the initial value to seed the picker with.
  */
+@RequiresApi(Build.VERSION_CODES.O)
 @Composable
 public fun TimePickerWith12HourClock(
     onTimeConfirm: (LocalTime) -> Unit,
@@ -464,6 +468,7 @@
  * @param fromDate the minimum date to be selected in picker
  * @param toDate the maximum date to be selected in picker
  */
+@RequiresApi(Build.VERSION_CODES.O)
 @Composable
 public fun DatePicker(
     onDateConfirm: (LocalDate) -> Unit,
@@ -856,17 +861,20 @@
     }
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun verifyDates(date: LocalDate, fromDate: LocalDate, toDate: LocalDate) {
     require(toDate >= fromDate) { "toDate should be greater than or equal to fromDate" }
     require(date in fromDate..toDate) { "date should lie between fromDate and toDate" }
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 private fun getMonthNames(pattern: String): List<String> {
     val monthFormatter = DateTimeFormatter.ofPattern(pattern)
     val months = 1..12
     return months.map { LocalDate.of(2022, it, 1).format(monthFormatter) }
 }
 
+@RequiresApi(Build.VERSION_CODES.O)
 internal class DatePickerState
 constructor(
     private val date: LocalDate,
diff --git a/wear/compose/integration-tests/macrobenchmark-target/build.gradle b/wear/compose/integration-tests/macrobenchmark-target/build.gradle
index d10859a..dbbe105 100644
--- a/wear/compose/integration-tests/macrobenchmark-target/build.gradle
+++ b/wear/compose/integration-tests/macrobenchmark-target/build.gradle
@@ -53,4 +53,4 @@
     implementation(project(":tracing:tracing-perfetto-binary"))
 }
 
-android.defaultConfig.minSdk = 30
+android.defaultConfig.minSdk = 25
diff --git a/wear/compose/integration-tests/macrobenchmark/build.gradle b/wear/compose/integration-tests/macrobenchmark/build.gradle
index e124928..6526e1b 100644
--- a/wear/compose/integration-tests/macrobenchmark/build.gradle
+++ b/wear/compose/integration-tests/macrobenchmark/build.gradle
@@ -23,7 +23,7 @@
 android {
     compileSdk = 35
     defaultConfig {
-        minSdk = 30
+        minSdk = 29
     }
     namespace = "androidx.wear.compose.integration.macrobenchmark"
     targetProjectPath = ":wear:compose:integration-tests:macrobenchmark-target"
diff --git a/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/MaterialGoldenTest.kt b/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/MaterialGoldenTest.kt
index 12a274b..f05c1b6 100644
--- a/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/MaterialGoldenTest.kt
+++ b/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/MaterialGoldenTest.kt
@@ -33,8 +33,27 @@
         AndroidXScreenshotTestRule("wear/protolayout/protolayout-material3")
 
     @Test
-    fun test() {
-        RunnerUtils.runSingleScreenshotTest(mScreenshotRule, testCase, expected)
+    fun testLtr() {
+        // Skip test if it's not meant for LTR
+        if (!testCase.isForLtr) return
+        RunnerUtils.runSingleScreenshotTest(
+            rule = mScreenshotRule,
+            layout = testCase.layout,
+            expected = expected,
+            isRtlDirection = false
+        )
+    }
+
+    @Test
+    fun testRtl() {
+        // Skip test if it's not meant for RTL
+        if (!testCase.isForRtl) return
+        RunnerUtils.runSingleScreenshotTest(
+            rule = mScreenshotRule,
+            layout = testCase.layout,
+            expected = expected + "_rtl",
+            isRtlDirection = true
+        )
     }
 
     companion object {
diff --git a/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/RunnerUtils.kt b/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/RunnerUtils.kt
index 6bf8296..5ee111f 100644
--- a/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/RunnerUtils.kt
+++ b/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/RunnerUtils.kt
@@ -33,27 +33,9 @@
     // watch dimensions here.
     const val SCREEN_SIZE_SMALL: Int = 525 // ~199dp
 
-    fun runSingleScreenshotTest(
-        rule: AndroidXScreenshotTestRule,
-        testCase: TestCase,
-        expected: String
-    ) {
-        if (testCase.isForLtr) {
-            runSingleScreenshotTest(rule, testCase.mLayout, expected, /* isRtlDirection= */ false)
-        }
-        if (testCase.isForRtl) {
-            runSingleScreenshotTest(
-                rule,
-                testCase.mLayout,
-                expected + "_rtl",
-                /* isRtlDirection= */ true
-            )
-        }
-    }
-
     @SuppressLint("BanThreadSleep")
     // TODO: b/355417923 - Avoid calling sleep.
-    private fun runSingleScreenshotTest(
+    fun runSingleScreenshotTest(
         rule: AndroidXScreenshotTestRule,
         layout: LayoutElementBuilders.Layout,
         expected: String,
@@ -61,7 +43,7 @@
     ) {
         val layoutPayload = layout.toByteArray()
 
-        val startIntent: Intent =
+        val startIntent =
             Intent(
                 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
                     .targetContext,
@@ -134,7 +116,7 @@
 
     /** Holds testcase parameters. */
     class TestCase(
-        val mLayout: LayoutElementBuilders.Layout,
+        val layout: LayoutElementBuilders.Layout,
         val isForRtl: Boolean,
         val isForLtr: Boolean
     )
diff --git a/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/TestCasesGenerator.kt b/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/TestCasesGenerator.kt
index 1826c42..6b02da5 100644
--- a/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/TestCasesGenerator.kt
+++ b/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/TestCasesGenerator.kt
@@ -23,11 +23,7 @@
 import androidx.wear.protolayout.DimensionBuilders.dp
 import androidx.wear.protolayout.DimensionBuilders.expand
 import androidx.wear.protolayout.LayoutElementBuilders
-import androidx.wear.protolayout.LayoutElementBuilders.Box
 import androidx.wear.protolayout.LayoutElementBuilders.Column
-import androidx.wear.protolayout.ModifiersBuilders.Background
-import androidx.wear.protolayout.ModifiersBuilders.Corner
-import androidx.wear.protolayout.ModifiersBuilders.Modifiers
 import androidx.wear.protolayout.expression.VersionBuilders.VersionInfo
 import androidx.wear.protolayout.material3.AppCardStyle.Companion.largeAppCardStyle
 import androidx.wear.protolayout.material3.ButtonDefaults.filledButtonColors
@@ -48,7 +44,6 @@
 import androidx.wear.protolayout.modifiers.clickable
 import androidx.wear.protolayout.modifiers.clip
 import androidx.wear.protolayout.modifiers.contentDescription
-import androidx.wear.protolayout.types.LayoutColor
 import androidx.wear.protolayout.types.layoutString
 import com.google.common.collect.ImmutableMap
 
@@ -491,19 +486,6 @@
         return collectTestCases(testCases)
     }
 
-    private fun coloredBox(color: LayoutColor, shape: Corner) =
-        Box.Builder()
-            .setWidth(expand())
-            .setHeight(expand())
-            .setModifiers(
-                Modifiers.Builder()
-                    .setBackground(
-                        Background.Builder().setColor(color.prop).setCorner(shape).build()
-                    )
-                    .build()
-            )
-            .build()
-
     private fun collectTestCases(
         testCases: Map<String, LayoutElementBuilders.LayoutElement>
     ): ImmutableMap<String, LayoutElementBuilders.Layout> {