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> {