Merge "Add Material 3 Benchmarks for Chip" into androidx-main
diff --git a/compose/material3/benchmark/build.gradle b/compose/material3/benchmark/build.gradle
index c21a27e..df3e144 100644
--- a/compose/material3/benchmark/build.gradle
+++ b/compose/material3/benchmark/build.gradle
@@ -24,6 +24,7 @@
 
 dependencies {
 
+    androidTestImplementation(project(":compose:material:material-icons-core"))
     androidTestImplementation(project(":compose:material3:material3"))
     androidTestImplementation(project(":benchmark:benchmark-junit4"))
     androidTestImplementation(project(":compose:runtime:runtime"))
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/ChipBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/ChipBenchmark.kt
new file mode 100644
index 0000000..d190cbf
--- /dev/null
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/ChipBenchmark.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.benchmark
+
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material3.AssistChip
+import androidx.compose.material3.AssistChipDefaults
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.LayeredComposeTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.benchmarkFirstCompose
+import androidx.compose.testutils.benchmark.benchmarkFirstDraw
+import androidx.compose.testutils.benchmark.benchmarkFirstLayout
+import androidx.compose.testutils.benchmark.benchmarkFirstMeasure
+import androidx.compose.ui.Modifier
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class ChipBenchmark {
+
+    @get:Rule
+    val benchmarkRule = ComposeBenchmarkRule()
+
+    private val chipTestCaseFactory = { ChipTestCase() }
+
+    @Test
+    fun first_compose() {
+        benchmarkRule.benchmarkFirstCompose(chipTestCaseFactory)
+    }
+
+    @Test
+    fun chip_measure() {
+        benchmarkRule.benchmarkFirstMeasure(chipTestCaseFactory)
+    }
+
+    @Test
+    fun chip_layout() {
+        benchmarkRule.benchmarkFirstLayout(chipTestCaseFactory)
+    }
+
+    @Test
+    fun chip_draw() {
+        benchmarkRule.benchmarkFirstDraw(chipTestCaseFactory)
+    }
+}
+
+internal class ChipTestCase : LayeredComposeTestCase() {
+
+    @OptIn(ExperimentalMaterial3Api::class)
+    @Composable
+    override fun MeasuredContent() {
+        AssistChip(
+            onClick = { /* Do something! */ },
+            label = { Text("Assist Chip") },
+            leadingIcon = {
+                Icon(
+                    Icons.Filled.Settings,
+                    contentDescription = "Localized description",
+                    Modifier.size(AssistChipDefaults.IconSize)
+                )
+            }
+        )
+    }
+
+    @Composable
+    override fun ContentWrappers(content: @Composable () -> Unit) {
+        MaterialTheme {
+            content()
+        }
+    }
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
index 1c5ef84..b2c7357 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
@@ -43,12 +43,14 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.graphicsLayer
@@ -1493,6 +1495,7 @@
         interactionSource: InteractionSource
     ): State<Dp> {
         val interactions = remember { mutableStateListOf<Interaction>() }
+        var lastInteraction by remember { mutableStateOf<Interaction?>(null) }
         LaunchedEffect(interactionSource) {
             interactionSource.interactions.collect { interaction ->
                 when (interaction) {
@@ -1546,22 +1549,16 @@
 
         val animatable = remember { Animatable(target, Dp.VectorConverter) }
 
-        if (!enabled) {
-            // No transition when moving to a disabled state
-            LaunchedEffect(target) { animatable.snapTo(target) }
-        } else {
-            LaunchedEffect(target) {
-                val lastInteraction = when (animatable.targetValue) {
-                    pressedElevation -> PressInteraction.Press(Offset.Zero)
-                    hoveredElevation -> HoverInteraction.Enter()
-                    focusedElevation -> FocusInteraction.Focus()
-                    draggedElevation -> DragInteraction.Start()
-                    else -> null
-                }
+        LaunchedEffect(target) {
+            if (!enabled) {
+                // No transition when moving to a disabled state
+                animatable.snapTo(target)
+            } else {
                 animatable.animateElevation(
                     from = lastInteraction, to = interaction, target = target
                 )
             }
+            lastInteraction = interaction
         }
 
         return animatable.asState()
@@ -1653,6 +1650,7 @@
         interactionSource: InteractionSource
     ): State<Dp> {
         val interactions = remember { mutableStateListOf<Interaction>() }
+        var lastInteraction by remember { mutableStateOf<Interaction?>(null) }
         LaunchedEffect(interactionSource) {
             interactionSource.interactions.collect { interaction ->
                 when (interaction) {
@@ -1706,22 +1704,16 @@
 
         val animatable = remember { Animatable(target, Dp.VectorConverter) }
 
-        if (!enabled) {
-            // No transition when moving to a disabled state
-            LaunchedEffect(target) { animatable.snapTo(target) }
-        } else {
-            LaunchedEffect(target) {
-                val lastInteraction = when (animatable.targetValue) {
-                    pressedElevation -> PressInteraction.Press(Offset.Zero)
-                    hoveredElevation -> HoverInteraction.Enter()
-                    focusedElevation -> FocusInteraction.Focus()
-                    draggedElevation -> DragInteraction.Start()
-                    else -> null
-                }
+        LaunchedEffect(target) {
+            if (!enabled) {
+                // No transition when moving to a disabled state
+                animatable.snapTo(target)
+            } else {
                 animatable.animateElevation(
                     from = lastInteraction, to = interaction, target = target
                 )
             }
+            lastInteraction = interaction
         }
 
         return animatable.asState()