Merge "Add property getter annotation target, as this was allowed in the Java version of @Query" into androidx-main
diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml
index 6a0bcc3..3321dd7 100644
--- a/.github/workflows/integration_tests.yml
+++ b/.github/workflows/integration_tests.yml
@@ -21,7 +21,7 @@
       - id: run_tests
         uses: androidX/androidx-ci-action@dist-latest
         with:
-          run-id: ${{ github.event.workflow_run.id }}
+          target-run-id: ${{ github.event.workflow_run.id }}
           gcp-token: ${{ secrets.GCP_SA_KEY }}
           github-token: ${{ secrets.GITHUB_TOKEN }}
           output-folder: ${{ steps.dirs.outputs.output-dir }}
diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml
index 1f884f6..ce47ba8 100644
--- a/.github/workflows/presubmit.yml
+++ b/.github/workflows/presubmit.yml
@@ -17,14 +17,12 @@
       - name: "Setup global constants"
         id: global-constants
         # The configuration-cache cannot be used due to state excluded when caching Gradle User Home
-        # Agressively remove old build-cache entries to avoid uncontrolled growth of the Gradle User Home
         run: |
           set -x
           GRADLEW_FLAGS="-Dorg.gradle.internal.http.connectionTimeout=60000 \
             -Dorg.gradle.internal.http.socketTimeout=60000                  \
             -Dorg.gradle.internal.repository.max.retries=20                 \
             -Dorg.gradle.internal.repository.initial.backoff=500            \
-            -Dgradle.cache.local.removeUnusedEntriesAfterDays=P1D           \
             --no-configuration-cache                                        \
             --stacktrace"
           echo "::set-output name=gradlew_flags::$GRADLEW_FLAGS"
diff --git a/README.md b/README.md
index 21a5938..d4fdb91 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
 # Android Jetpack
 
+[![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.androidx.dev)
+
 Jetpack is a suite of libraries, tools, and guidance to help developers write high-quality apps easier. These components help you follow best practices, free you from writing boilerplate code, and simplify complex tasks, so you can focus on the code you care about.
 
 Jetpack comprises the `androidx.*` package libraries, unbundled from the platform APIs. This means that it offers backward compatibility and is updated more frequently than the Android platform, making sure you always have access to the latest and greatest versions of the Jetpack components.
diff --git a/activity/activity-ktx/build.gradle b/activity/activity-ktx/build.gradle
index 8b86605..b7605b6 100644
--- a/activity/activity-ktx/build.gradle
+++ b/activity/activity-ktx/build.gradle
@@ -33,7 +33,7 @@
         because 'Mirror activity dependency graph for -ktx artifacts'
     }
     api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-ktx"))
-    api(projectOrArtifact(":savedstate:savedstate-ktx")) {
+    api("androidx.savedstate:savedstate-ktx:1.2.0-alpha01") {
         because 'Mirror activity dependency graph for -ktx artifacts'
     }
     api(libs.kotlinStdlib)
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index a5c92a8..ffcb466 100644
--- a/activity/activity/build.gradle
+++ b/activity/activity/build.gradle
@@ -20,7 +20,7 @@
     api(projectOrArtifact(":core:core"))
     api(projectOrArtifact(":lifecycle:lifecycle-runtime"))
     api(projectOrArtifact(":lifecycle:lifecycle-viewmodel"))
-    api(projectOrArtifact(":savedstate:savedstate"))
+    api("androidx.savedstate:savedstate:1.2.0-alpha01")
     api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-savedstate"))
     implementation("androidx.tracing:tracing:1.0.0")
     api(libs.kotlinStdlib)
diff --git a/ads/ads-identifier-benchmark/src/androidTest/AndroidManifest.xml b/ads/ads-identifier-benchmark/src/androidTest/AndroidManifest.xml
index e50ddd3..066d67e 100644
--- a/ads/ads-identifier-benchmark/src/androidTest/AndroidManifest.xml
+++ b/ads/ads-identifier-benchmark/src/androidTest/AndroidManifest.xml
@@ -16,9 +16,9 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.ads.identifier.benchmark">
+    package="androidx.ads.identifier.benchmark.test">
     <application
-        android:name=".AdsIdentifierBenchmarkApplication">
+        android:name="androidx.ads.identifier.benchmark.AdsIdentifierBenchmarkApplication">
         <!-- enable profiling by shell for non-intrusive profiling tools -->
         <profileable android:shell="true"/>
     </application>
diff --git a/annotation/annotation-experimental-lint/integration-tests/lint-baseline.xml b/annotation/annotation-experimental-lint/integration-tests/lint-baseline.xml
index 9f5f5d8..ff3d158 100644
--- a/annotation/annotation-experimental-lint/integration-tests/lint-baseline.xml
+++ b/annotation/annotation-experimental-lint/integration-tests/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-beta02" type="baseline" client="cli" dependencies="false" name="AGP (7.1.0-beta02)" variant="all" version="7.1.0-beta02">
+<issues format="6" by="lint 7.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (7.3.0-alpha01)" variant="all" version="7.3.0-alpha01">
 
     <issue
         id="ExperimentalAnnotationRetention"
@@ -510,6 +510,50 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@sample.experimental.foo.ExperimentalPackage` or `@OptIn(markerClass = sample.experimental.foo.ExperimentalPackage.class)`"
+        errorLine1="        Bar bar = new Bar();"
+        errorLine2="                  ~~~~~~~~~">
+        <location
+            file="src/main/java/sample/experimental/UseJavaPackageFromJava.java"
+            line="28"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="UnsafeOptInUsageError"
+        message="This declaration is opt-in and its usage should be marked with `@sample.experimental.foo.ExperimentalPackage` or `@OptIn(markerClass = sample.experimental.foo.ExperimentalPackage.class)`"
+        errorLine1="        bar.baz();"
+        errorLine2="            ~~~">
+        <location
+            file="src/main/java/sample/experimental/UseJavaPackageFromJava.java"
+            line="29"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="UnsafeOptInUsageError"
+        message="This declaration is opt-in and its usage should be marked with `@sample.optin.ExperimentalJavaAnnotation` or `@OptIn(markerClass = sample.optin.ExperimentalJavaAnnotation.class)`"
+        errorLine1="        AnnotatedJavaPackage experimentalObject = new AnnotatedJavaPackage();"
+        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/main/java/sample/optin/UseJavaPackageFromJava.java"
+            line="33"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="UnsafeOptInUsageError"
+        message="This declaration is opt-in and its usage should be marked with `@sample.optin.ExperimentalJavaAnnotation` or `@OptIn(markerClass = sample.optin.ExperimentalJavaAnnotation.class)`"
+        errorLine1="        experimentalObject.method();"
+        errorLine2="                           ~~~~~~">
+        <location
+            file="src/main/java/sample/optin/UseJavaPackageFromJava.java"
+            line="34"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="UnsafeOptInUsageError"
+        message="This declaration is opt-in and its usage should be marked with `@sample.experimental.foo.ExperimentalPackage` or `@OptIn(markerClass = sample.experimental.foo.ExperimentalPackage.class)`"
         errorLine1="        callPackageExperimental();"
         errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
@@ -531,6 +575,50 @@
 
     <issue
         id="UnsafeOptInUsageError"
+        message="This declaration is opt-in and its usage should be marked with `@sample.optin.ExperimentalJavaAnnotation` or `@OptIn(markerClass = sample.optin.ExperimentalJavaAnnotation.class)`"
+        errorLine1="        val experimentalObject = AnnotatedJavaPackage()"
+        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/main/java/sample/optin/UseJavaPackageFromKt.kt"
+            line="30"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="UnsafeOptInUsageError"
+        message="This declaration is opt-in and its usage should be marked with `@sample.optin.ExperimentalJavaAnnotation` or `@OptIn(markerClass = sample.optin.ExperimentalJavaAnnotation.class)`"
+        errorLine1="        experimentalObject.method()"
+        errorLine2="                           ~~~~~~">
+        <location
+            file="src/main/java/sample/optin/UseJavaPackageFromKt.kt"
+            line="31"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="UnsafeOptInUsageError"
+        message="This declaration is opt-in and its usage should be marked with `@sample.experimental.foo.ExperimentalPackage` or `@OptIn(markerClass = sample.experimental.foo.ExperimentalPackage.class)`"
+        errorLine1="        val bar = Bar()"
+        errorLine2="                  ~~~">
+        <location
+            file="src/main/java/sample/experimental/UseJavaPackageFromKt.kt"
+            line="32"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="UnsafeOptInUsageError"
+        message="This declaration is opt-in and its usage should be marked with `@sample.experimental.foo.ExperimentalPackage` or `@OptIn(markerClass = sample.experimental.foo.ExperimentalPackage.class)`"
+        errorLine1="        bar.baz()"
+        errorLine2="            ~~~">
+        <location
+            file="src/main/java/sample/experimental/UseJavaPackageFromKt.kt"
+            line="33"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@sample.experimental.foo.ExperimentalPackage` or `@OptIn(markerClass = sample.experimental.foo.ExperimentalPackage.class)`"
         errorLine1="        callPackageExperimental()"
         errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/appsearch/appsearch-local-storage/build.gradle b/appsearch/appsearch-local-storage/build.gradle
index 1e4fca1..ae507e5 100644
--- a/appsearch/appsearch-local-storage/build.gradle
+++ b/appsearch/appsearch-local-storage/build.gradle
@@ -75,6 +75,7 @@
     implementation(project(":appsearch:appsearch"))
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     implementation("androidx.core:core:1.2.0")
+    implementation('androidx.collection:collection:1.0.0')
 
     androidTestImplementation project(':appsearch:appsearch-test-util')
     androidTestImplementation(libs.multidex)
diff --git a/appsearch/appsearch/build.gradle b/appsearch/appsearch/build.gradle
index 86a1f1e..d91e47c 100644
--- a/appsearch/appsearch/build.gradle
+++ b/appsearch/appsearch/build.gradle
@@ -35,6 +35,7 @@
 
     implementation('androidx.concurrent:concurrent-futures:1.0.0')
     implementation('androidx.core:core:1.2.0')
+    implementation('androidx.collection:collection:1.0.0')
 
     androidTestAnnotationProcessor project(':appsearch:appsearch-compiler')
     androidTestImplementation project(':appsearch:appsearch-local-storage')
diff --git a/benchmark/benchmark-common/api/1.1.0-beta04.txt b/benchmark/benchmark-common/api/1.1.0-beta04.txt
new file mode 100644
index 0000000..8fd1366
--- /dev/null
+++ b/benchmark/benchmark-common/api/1.1.0-beta04.txt
@@ -0,0 +1,52 @@
+// Signature format: 4.0
+package androidx.benchmark {
+
+  @RequiresApi(21) public final class Api21Kt {
+  }
+
+  @RequiresApi(24) public final class Api24Kt {
+  }
+
+  @RequiresApi(27) public final class Api27Kt {
+  }
+
+  @RequiresApi(29) public final class Api29Kt {
+  }
+
+  public final class ArgumentsKt {
+  }
+
+  public final class BenchmarkState {
+    method public boolean keepRunning();
+    method public void pauseTiming();
+    method public void resumeTiming();
+    field public static final androidx.benchmark.BenchmarkState.Companion Companion;
+  }
+
+  public static final class BenchmarkState.Companion {
+  }
+
+  public final class ConfigurationErrorKt {
+  }
+
+  public final class MetricNameUtilsKt {
+  }
+
+  public final class ProfilerKt {
+  }
+
+  public final class UserspaceTracingKt {
+  }
+
+}
+
+package androidx.benchmark.perfetto {
+
+  public final class PerfettoConfigKt {
+  }
+
+  public final class UiStateKt {
+  }
+
+}
+
diff --git a/benchmark/benchmark-common/api/public_plus_experimental_1.1.0-beta04.txt b/benchmark/benchmark-common/api/public_plus_experimental_1.1.0-beta04.txt
new file mode 100644
index 0000000..fd9ccb2
--- /dev/null
+++ b/benchmark/benchmark-common/api/public_plus_experimental_1.1.0-beta04.txt
@@ -0,0 +1,57 @@
+// Signature format: 4.0
+package androidx.benchmark {
+
+  @RequiresApi(21) public final class Api21Kt {
+  }
+
+  @RequiresApi(24) public final class Api24Kt {
+  }
+
+  @RequiresApi(27) public final class Api27Kt {
+  }
+
+  @RequiresApi(29) public final class Api29Kt {
+  }
+
+  public final class ArgumentsKt {
+  }
+
+  public final class BenchmarkState {
+    method public boolean keepRunning();
+    method public void pauseTiming();
+    method @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public static void reportData(String className, String testName, @IntRange(from=0) long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
+    method public void resumeTiming();
+    field public static final androidx.benchmark.BenchmarkState.Companion Companion;
+  }
+
+  public static final class BenchmarkState.Companion {
+    method @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public void reportData(String className, String testName, @IntRange(from=0) long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
+  }
+
+  @kotlin.Experimental @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public static @interface BenchmarkState.Companion.ExperimentalExternalReport {
+  }
+
+  public final class ConfigurationErrorKt {
+  }
+
+  public final class MetricNameUtilsKt {
+  }
+
+  public final class ProfilerKt {
+  }
+
+  public final class UserspaceTracingKt {
+  }
+
+}
+
+package androidx.benchmark.perfetto {
+
+  public final class PerfettoConfigKt {
+  }
+
+  public final class UiStateKt {
+  }
+
+}
+
diff --git a/benchmark/benchmark-common/api/res-1.1.0-beta04.txt b/benchmark/benchmark-common/api/res-1.1.0-beta04.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/benchmark-common/api/res-1.1.0-beta04.txt
diff --git a/benchmark/benchmark-common/api/restricted_1.1.0-beta04.txt b/benchmark/benchmark-common/api/restricted_1.1.0-beta04.txt
new file mode 100644
index 0000000..fb74f25
--- /dev/null
+++ b/benchmark/benchmark-common/api/restricted_1.1.0-beta04.txt
@@ -0,0 +1,54 @@
+// Signature format: 4.0
+package androidx.benchmark {
+
+  @RequiresApi(21) public final class Api21Kt {
+  }
+
+  @RequiresApi(24) public final class Api24Kt {
+  }
+
+  @RequiresApi(27) public final class Api27Kt {
+  }
+
+  @RequiresApi(29) public final class Api29Kt {
+  }
+
+  public final class ArgumentsKt {
+  }
+
+  public final class BenchmarkState {
+    method public boolean keepRunning();
+    method @kotlin.PublishedApi internal boolean keepRunningInternal();
+    method public void pauseTiming();
+    method public void resumeTiming();
+    field public static final androidx.benchmark.BenchmarkState.Companion Companion;
+    field @kotlin.PublishedApi internal int iterationsRemaining;
+  }
+
+  public static final class BenchmarkState.Companion {
+  }
+
+  public final class ConfigurationErrorKt {
+  }
+
+  public final class MetricNameUtilsKt {
+  }
+
+  public final class ProfilerKt {
+  }
+
+  public final class UserspaceTracingKt {
+  }
+
+}
+
+package androidx.benchmark.perfetto {
+
+  public final class PerfettoConfigKt {
+  }
+
+  public final class UiStateKt {
+  }
+
+}
+
diff --git a/benchmark/benchmark-junit4/api/1.1.0-beta04.txt b/benchmark/benchmark-junit4/api/1.1.0-beta04.txt
new file mode 100644
index 0000000..873f105
--- /dev/null
+++ b/benchmark/benchmark-junit4/api/1.1.0-beta04.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+}
+
diff --git a/benchmark/benchmark-junit4/api/public_plus_experimental_1.1.0-beta04.txt b/benchmark/benchmark-junit4/api/public_plus_experimental_1.1.0-beta04.txt
new file mode 100644
index 0000000..873f105
--- /dev/null
+++ b/benchmark/benchmark-junit4/api/public_plus_experimental_1.1.0-beta04.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+}
+
diff --git a/benchmark/benchmark-junit4/api/res-1.1.0-beta04.txt b/benchmark/benchmark-junit4/api/res-1.1.0-beta04.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/benchmark-junit4/api/res-1.1.0-beta04.txt
diff --git a/benchmark/benchmark-junit4/api/restricted_1.1.0-beta04.txt b/benchmark/benchmark-junit4/api/restricted_1.1.0-beta04.txt
new file mode 100644
index 0000000..d3bf38c
--- /dev/null
+++ b/benchmark/benchmark-junit4/api/restricted_1.1.0-beta04.txt
@@ -0,0 +1,29 @@
+// Signature format: 4.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method @kotlin.PublishedApi internal androidx.benchmark.BenchmarkState getOuterState();
+    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class PerfettoRule implements org.junit.rules.TestRule {
+    ctor public PerfettoRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+  }
+
+}
+
diff --git a/benchmark/benchmark-macro-junit4/api/1.1.0-beta04.txt b/benchmark/benchmark-macro-junit4/api/1.1.0-beta04.txt
new file mode 100644
index 0000000..4b1ab53
--- /dev/null
+++ b/benchmark/benchmark-macro-junit4/api/1.1.0-beta04.txt
@@ -0,0 +1,14 @@
+// Signature format: 4.0
+package androidx.benchmark.macro.junit4 {
+
+  public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
+    ctor public MacrobenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+  }
+
+}
+
diff --git a/benchmark/benchmark-macro-junit4/api/public_plus_experimental_1.1.0-beta04.txt b/benchmark/benchmark-macro-junit4/api/public_plus_experimental_1.1.0-beta04.txt
new file mode 100644
index 0000000..31d9966
--- /dev/null
+++ b/benchmark/benchmark-macro-junit4/api/public_plus_experimental_1.1.0-beta04.txt
@@ -0,0 +1,20 @@
+// Signature format: 4.0
+package androidx.benchmark.macro.junit4 {
+
+  @RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
+  public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
+    ctor public MacrobenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+  }
+
+}
+
diff --git a/benchmark/benchmark-macro-junit4/api/res-1.1.0-beta04.txt b/benchmark/benchmark-macro-junit4/api/res-1.1.0-beta04.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/benchmark-macro-junit4/api/res-1.1.0-beta04.txt
diff --git a/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta04.txt b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta04.txt
new file mode 100644
index 0000000..4b1ab53
--- /dev/null
+++ b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta04.txt
@@ -0,0 +1,14 @@
+// Signature format: 4.0
+package androidx.benchmark.macro.junit4 {
+
+  public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
+    ctor public MacrobenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+  }
+
+}
+
diff --git a/benchmark/benchmark-macro/api/1.1.0-beta04.txt b/benchmark/benchmark-macro/api/1.1.0-beta04.txt
new file mode 100644
index 0000000..ad0beca
--- /dev/null
+++ b/benchmark/benchmark-macro/api/1.1.0-beta04.txt
@@ -0,0 +1,105 @@
+// Signature format: 4.0
+package androidx.benchmark.macro {
+
+  @RequiresApi(29) public final class Api29Kt {
+  }
+
+  public enum BaselineProfileMode {
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Disable;
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Require;
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode UseIfAvailable;
+  }
+
+  public final class BaselineProfilesKt {
+  }
+
+  public abstract sealed class CompilationMode {
+    field public static final androidx.benchmark.macro.CompilationMode.Companion Companion;
+    field public static final androidx.benchmark.macro.CompilationMode DEFAULT;
+  }
+
+  public static final class CompilationMode.Companion {
+  }
+
+  public static final class CompilationMode.Full extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Full();
+  }
+
+  @RequiresApi(24) public static final class CompilationMode.None extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.None();
+  }
+
+  @RequiresApi(24) public static final class CompilationMode.Partial extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode, optional @IntRange(from=0) int warmupIterations);
+    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode);
+    ctor public CompilationMode.Partial();
+    method public androidx.benchmark.macro.BaselineProfileMode getBaselineProfileMode();
+    method public int getWarmupIterations();
+    property public final androidx.benchmark.macro.BaselineProfileMode baselineProfileMode;
+    property public final int warmupIterations;
+  }
+
+  public final class CompilationModeKt {
+  }
+
+  public final class FrameTimingMetric extends androidx.benchmark.macro.Metric {
+    ctor public FrameTimingMetric();
+  }
+
+  public final class IdeSummaryStringKt {
+  }
+
+  public final class IterationResultKt {
+  }
+
+  public final class MacrobenchmarkKt {
+  }
+
+  public final class MacrobenchmarkScope {
+    ctor public MacrobenchmarkScope(String packageName, boolean launchWithClearTask);
+    method public void dropKernelPageCache();
+    method public androidx.test.uiautomator.UiDevice getDevice();
+    method public Integer? getIteration();
+    method public String getPackageName();
+    method public void killProcess();
+    method public void pressHome(optional long delayDurationMs);
+    method public void pressHome();
+    method public void startActivityAndWait(optional kotlin.jvm.functions.Function1<? super android.content.Intent,kotlin.Unit> block);
+    method public void startActivityAndWait();
+    method public void startActivityAndWait(android.content.Intent intent);
+    property public final androidx.test.uiautomator.UiDevice device;
+    property public final Integer? iteration;
+    property public final String packageName;
+  }
+
+  public abstract sealed class Metric {
+  }
+
+  public final class MetricKt {
+  }
+
+  public final class MetricResultExtensionsKt {
+  }
+
+  public enum StartupMode {
+    enum_constant public static final androidx.benchmark.macro.StartupMode COLD;
+    enum_constant public static final androidx.benchmark.macro.StartupMode HOT;
+    enum_constant public static final androidx.benchmark.macro.StartupMode WARM;
+  }
+
+  public final class StartupTimingMetric extends androidx.benchmark.macro.Metric {
+    ctor public StartupTimingMetric();
+  }
+
+  public final class TagKt {
+  }
+
+}
+
+package androidx.benchmark.macro.perfetto {
+
+  public final class ForceTracingKt {
+  }
+
+}
+
diff --git a/benchmark/benchmark-macro/api/public_plus_experimental_1.1.0-beta04.txt b/benchmark/benchmark-macro/api/public_plus_experimental_1.1.0-beta04.txt
new file mode 100644
index 0000000..4e526b3
--- /dev/null
+++ b/benchmark/benchmark-macro/api/public_plus_experimental_1.1.0-beta04.txt
@@ -0,0 +1,119 @@
+// Signature format: 4.0
+package androidx.benchmark.macro {
+
+  @RequiresApi(29) public final class Api29Kt {
+  }
+
+  @RequiresApi(23) @androidx.benchmark.macro.ExperimentalMetricApi public final class AudioUnderrunMetric extends androidx.benchmark.macro.Metric {
+    ctor public AudioUnderrunMetric();
+  }
+
+  public enum BaselineProfileMode {
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Disable;
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Require;
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode UseIfAvailable;
+  }
+
+  public final class BaselineProfilesKt {
+  }
+
+  public abstract sealed class CompilationMode {
+    field public static final androidx.benchmark.macro.CompilationMode.Companion Companion;
+    field public static final androidx.benchmark.macro.CompilationMode DEFAULT;
+  }
+
+  public static final class CompilationMode.Companion {
+  }
+
+  public static final class CompilationMode.Full extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Full();
+  }
+
+  @RequiresApi(24) public static final class CompilationMode.None extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.None();
+  }
+
+  @RequiresApi(24) public static final class CompilationMode.Partial extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode, optional @IntRange(from=0) int warmupIterations);
+    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode);
+    ctor public CompilationMode.Partial();
+    method public androidx.benchmark.macro.BaselineProfileMode getBaselineProfileMode();
+    method public int getWarmupIterations();
+    property public final androidx.benchmark.macro.BaselineProfileMode baselineProfileMode;
+    property public final int warmupIterations;
+  }
+
+  public final class CompilationModeKt {
+  }
+
+  @kotlin.RequiresOptIn(message="The Baseline profile generation API is experimental.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalBaselineProfilesApi {
+  }
+
+  @kotlin.RequiresOptIn(message="This Metric API is experimental.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalMetricApi {
+  }
+
+  public final class FrameTimingMetric extends androidx.benchmark.macro.Metric {
+    ctor public FrameTimingMetric();
+  }
+
+  public final class IdeSummaryStringKt {
+  }
+
+  public final class IterationResultKt {
+  }
+
+  public final class MacrobenchmarkKt {
+  }
+
+  public final class MacrobenchmarkScope {
+    ctor public MacrobenchmarkScope(String packageName, boolean launchWithClearTask);
+    method public void dropKernelPageCache();
+    method public androidx.test.uiautomator.UiDevice getDevice();
+    method public Integer? getIteration();
+    method public String getPackageName();
+    method public void killProcess();
+    method public void pressHome(optional long delayDurationMs);
+    method public void pressHome();
+    method public void startActivityAndWait(optional kotlin.jvm.functions.Function1<? super android.content.Intent,kotlin.Unit> block);
+    method public void startActivityAndWait();
+    method public void startActivityAndWait(android.content.Intent intent);
+    property public final androidx.test.uiautomator.UiDevice device;
+    property public final Integer? iteration;
+    property public final String packageName;
+  }
+
+  public abstract sealed class Metric {
+  }
+
+  public final class MetricKt {
+  }
+
+  public final class MetricResultExtensionsKt {
+  }
+
+  public enum StartupMode {
+    enum_constant public static final androidx.benchmark.macro.StartupMode COLD;
+    enum_constant public static final androidx.benchmark.macro.StartupMode HOT;
+    enum_constant public static final androidx.benchmark.macro.StartupMode WARM;
+  }
+
+  public final class StartupTimingMetric extends androidx.benchmark.macro.Metric {
+    ctor public StartupTimingMetric();
+  }
+
+  public final class TagKt {
+  }
+
+  @RequiresApi(29) @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
+    ctor public TraceSectionMetric(String sectionName);
+  }
+
+}
+
+package androidx.benchmark.macro.perfetto {
+
+  public final class ForceTracingKt {
+  }
+
+}
+
diff --git a/benchmark/benchmark-macro/api/res-1.1.0-beta04.txt b/benchmark/benchmark-macro/api/res-1.1.0-beta04.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/benchmark-macro/api/res-1.1.0-beta04.txt
diff --git a/benchmark/benchmark-macro/api/restricted_1.1.0-beta04.txt b/benchmark/benchmark-macro/api/restricted_1.1.0-beta04.txt
new file mode 100644
index 0000000..b1786ae
--- /dev/null
+++ b/benchmark/benchmark-macro/api/restricted_1.1.0-beta04.txt
@@ -0,0 +1,118 @@
+// Signature format: 4.0
+package androidx.benchmark.macro {
+
+  @RequiresApi(29) public final class Api29Kt {
+  }
+
+  public enum BaselineProfileMode {
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Disable;
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Require;
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode UseIfAvailable;
+  }
+
+  public final class BaselineProfilesKt {
+    method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void collectBaselineProfile(String uniqueName, String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
+  public abstract sealed class CompilationMode {
+    field public static final androidx.benchmark.macro.CompilationMode.Companion Companion;
+    field public static final androidx.benchmark.macro.CompilationMode DEFAULT;
+  }
+
+  public static final class CompilationMode.Companion {
+  }
+
+  public static final class CompilationMode.Full extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Full();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final class CompilationMode.Interpreted extends androidx.benchmark.macro.CompilationMode {
+  }
+
+  @RequiresApi(24) public static final class CompilationMode.None extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.None();
+  }
+
+  @RequiresApi(24) public static final class CompilationMode.Partial extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode, optional @IntRange(from=0) int warmupIterations);
+    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode);
+    ctor public CompilationMode.Partial();
+    method public androidx.benchmark.macro.BaselineProfileMode getBaselineProfileMode();
+    method public int getWarmupIterations();
+    property public final androidx.benchmark.macro.BaselineProfileMode baselineProfileMode;
+    property public final int warmupIterations;
+  }
+
+  public final class CompilationModeKt {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static boolean isSupportedWithVmSettings(androidx.benchmark.macro.CompilationMode);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class FrameTimingGfxInfoMetric extends androidx.benchmark.macro.Metric {
+    ctor public FrameTimingGfxInfoMetric();
+  }
+
+  public final class FrameTimingMetric extends androidx.benchmark.macro.Metric {
+    ctor public FrameTimingMetric();
+  }
+
+  public final class IdeSummaryStringKt {
+  }
+
+  public final class IterationResultKt {
+  }
+
+  public final class MacrobenchmarkKt {
+  }
+
+  public final class MacrobenchmarkScope {
+    ctor public MacrobenchmarkScope(String packageName, boolean launchWithClearTask);
+    method public void dropKernelPageCache();
+    method public androidx.test.uiautomator.UiDevice getDevice();
+    method public Integer? getIteration();
+    method public String getPackageName();
+    method public void killProcess();
+    method public void pressHome(optional long delayDurationMs);
+    method public void pressHome();
+    method public void startActivityAndWait(optional kotlin.jvm.functions.Function1<? super android.content.Intent,kotlin.Unit> block);
+    method public void startActivityAndWait();
+    method public void startActivityAndWait(android.content.Intent intent);
+    property public final androidx.test.uiautomator.UiDevice device;
+    property public final Integer? iteration;
+    property public final String packageName;
+  }
+
+  public abstract sealed class Metric {
+  }
+
+  public final class MetricKt {
+  }
+
+  public final class MetricResultExtensionsKt {
+  }
+
+  public enum StartupMode {
+    enum_constant public static final androidx.benchmark.macro.StartupMode COLD;
+    enum_constant public static final androidx.benchmark.macro.StartupMode HOT;
+    enum_constant public static final androidx.benchmark.macro.StartupMode WARM;
+  }
+
+  @RequiresApi(29) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class StartupTimingLegacyMetric extends androidx.benchmark.macro.Metric {
+    ctor public StartupTimingLegacyMetric();
+  }
+
+  public final class StartupTimingMetric extends androidx.benchmark.macro.Metric {
+    ctor public StartupTimingMetric();
+  }
+
+  public final class TagKt {
+  }
+
+}
+
+package androidx.benchmark.macro.perfetto {
+
+  public final class ForceTracingKt {
+  }
+
+}
+
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/public/src/main/kotlin/androidx/build/LibraryVersions.kt
index 622d653..35900d9 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -20,16 +20,16 @@
  * The list of versions codes of all the libraries in this project.
  */
 object LibraryVersions {
-    val ACTIVITY = Version("1.5.0-alpha02")
+    val ACTIVITY = Version("1.5.0-alpha03")
     val ADS_IDENTIFIER = Version("1.0.0-alpha05")
-    val ANNOTATION = Version("1.4.0-alpha02")
+    val ANNOTATION = Version("1.4.0-alpha03")
     val ANNOTATION_EXPERIMENTAL = Version("1.3.0-alpha01")
     val APPCOMPAT = Version("1.5.0-alpha01")
     val APPSEARCH = Version("1.0.0-alpha05")
     val ARCH_CORE = Version("2.2.0-alpha01")
     val ASYNCLAYOUTINFLATER = Version("1.1.0-alpha01")
     val AUTOFILL = Version("1.2.0-beta02")
-    val BENCHMARK = Version("1.1.0-beta03")
+    val BENCHMARK = Version("1.1.0-beta04")
     val BIOMETRIC = Version("1.2.0-alpha05")
     val BROWSER = Version("1.5.0-alpha01")
     val BUILDSRC_TESTS = Version("1.0.0-alpha01")
@@ -40,10 +40,10 @@
     val COLLECTION = Version("1.3.0-alpha01")
     val COLLECTION2 = Version("1.3.0-alpha01")
     val CONTENTPAGER = Version("1.1.0-alpha01")
-    val COMPOSE_MATERIAL3 = Version(System.getenv("COMPOSE_CUSTOM_VERSION") ?: "1.0.0-alpha05")
-    val COMPOSE = Version(System.getenv("COMPOSE_CUSTOM_VERSION") ?: "1.2.0-alpha03")
+    val COMPOSE_MATERIAL3 = Version(System.getenv("COMPOSE_CUSTOM_VERSION") ?: "1.0.0-alpha06")
+    val COMPOSE = Version(System.getenv("COMPOSE_CUSTOM_VERSION") ?: "1.2.0-alpha04")
     val COORDINATORLAYOUT = Version("1.3.0-alpha01")
-    val CORE = Version("1.8.0-alpha04")
+    val CORE = Version("1.8.0-alpha05")
     val CORE_ANIMATION = Version("1.0.0-alpha03")
     val CORE_ANIMATION_TESTING = Version("1.0.0-alpha03")
     val CORE_APPDIGEST = Version("1.0.0-alpha01")
@@ -61,10 +61,10 @@
     val DYNAMICANIMATION = Version("1.1.0-alpha04")
     val DYNAMICANIMATION_KTX = Version("1.0.0-alpha04")
     val EMOJI = Version("1.2.0-alpha03")
-    val EMOJI2 = Version("1.1.0-rc01")
+    val EMOJI2 = Version("1.2.0-alpha01")
     val ENTERPRISE = Version("1.1.0-rc01")
     val EXIFINTERFACE = Version("1.4.0-alpha01")
-    val FRAGMENT = Version("1.5.0-alpha02")
+    val FRAGMENT = Version("1.5.0-alpha03")
     val FUTURES = Version("1.2.0-alpha01")
     val GLANCE_TEMPLATE = Version("1.0.0-alpha01")
     val GLANCE = Version("1.0.0-alpha03")
@@ -84,15 +84,15 @@
     val LEANBACK_GRID = Version("1.0.0-alpha02")
     val LEGACY = Version("1.1.0-alpha01")
     val LIBYUV = Version("0.1.0-dev01")
-    val LIFECYCLE = Version("2.5.0-alpha02")
-    val LIFECYCLE_VIEWMODEL_COMPOSE = Version("2.5.0-alpha02")
+    val LIFECYCLE = Version("2.5.0-alpha03")
+    val LIFECYCLE_VIEWMODEL_COMPOSE = Version("2.5.0-alpha03")
     val LIFECYCLE_EXTENSIONS = Version("2.2.0")
     val LOADER = Version("1.2.0-alpha01")
     val MEDIA = Version("1.6.0-alpha01")
     val MEDIA2 = Version("1.3.0-alpha01")
     val MEDIAROUTER = Version("1.3.0-alpha02")
-    val METRICS = Version("1.0.0-alpha01")
-    val NAVIGATION = Version("2.5.0-alpha02")
+    val METRICS = Version("1.0.0-alpha02")
+    val NAVIGATION = Version("2.5.0-alpha03")
     val PAGING = Version("3.2.0-alpha01")
     val PAGING_COMPOSE = Version("1.0.0-alpha15")
     val PALETTE = Version("1.1.0-alpha01")
@@ -118,7 +118,7 @@
     val SLICE_BUILDERS_KTX = Version("1.0.0-alpha08")
     val SLICE_REMOTECALLBACK = Version("1.0.0-alpha01")
     val SLIDINGPANELAYOUT = Version("1.3.0-alpha01")
-    val STARTUP = Version("1.2.0-alpha01")
+    val STARTUP = Version("1.2.0-alpha02")
     val SQLITE = Version("2.3.0-alpha01")
     val SQLITE_INSPECTOR = Version("2.1.0-alpha01")
     val SWIPEREFRESHLAYOUT = Version("1.2.0-alpha01")
@@ -137,14 +137,14 @@
     val VIEWPAGER = Version("1.1.0-alpha02")
     val VIEWPAGER2 = Version("1.1.0-beta02")
     val WEAR = Version("1.3.0-alpha02")
-    val WEAR_COMPOSE = Version("1.0.0-alpha16")
+    val WEAR_COMPOSE = Version("1.0.0-alpha17")
     val WEAR_INPUT = Version("1.2.0-alpha03")
     val WEAR_INPUT_TESTING = WEAR_INPUT
     val WEAR_ONGOING = Version("1.1.0-alpha01")
     val WEAR_PHONE_INTERACTIONS = Version("1.1.0-alpha03")
     val WEAR_REMOTE_INTERACTIONS = Version("1.1.0-alpha01")
-    val WEAR_TILES = Version("1.1.0-alpha02")
-    val WEAR_WATCHFACE = Version("1.1.0-alpha03")
+    val WEAR_TILES = Version("1.1.0-alpha03")
+    val WEAR_WATCHFACE = Version("1.1.0-alpha04")
     val WEBKIT = Version("1.5.0-alpha01")
     val WINDOW = Version("1.1.0-alpha01")
     val WINDOW_EXTENSIONS = Version("1.1.0-alpha01")
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/SupportedQualitiesVerificationTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/SupportedQualitiesVerificationTest.kt
new file mode 100644
index 0000000..a9a76e3
--- /dev/null
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/SupportedQualitiesVerificationTest.kt
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2022 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.
+ */
+
+/*
+ * Copyright 2022 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.camera.video
+
+import android.content.Context
+import android.os.Build
+import androidx.camera.core.Camera
+import androidx.camera.core.CameraInfo
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.fakes.FakeLifecycleOwner
+import androidx.core.util.Consumer
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth
+import java.io.File
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = 21)
+class SupportedQualitiesVerificationTest(
+    private var cameraSelector: CameraSelector,
+    private var quality: Quality,
+) {
+
+    @get:Rule
+    val cameraRule = CameraUtil.grantCameraPermissionAndPreTest()
+
+    companion object {
+        private const val VIDEO_TIMEOUT_SEC = 10L
+
+        @JvmStatic
+        private val cameraSelectors =
+            arrayOf(CameraSelector.DEFAULT_BACK_CAMERA, CameraSelector.DEFAULT_FRONT_CAMERA)
+
+        @JvmStatic
+        private val quality = arrayOf(
+            Quality.SD,
+            Quality.HD,
+            Quality.FHD,
+            Quality.UHD,
+            Quality.LOWEST,
+            Quality.HIGHEST,
+        )
+
+        @JvmStatic
+        @Parameterized.Parameters(name = "cameraSelector={0}, quality={1}")
+        fun data() = mutableListOf<Array<Any?>>().apply {
+            cameraSelectors.forEach { cameraSelector ->
+                quality.forEach { quality ->
+                    add(arrayOf(cameraSelector, quality))
+                }
+            }
+        }
+    }
+
+    private val instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val context: Context = ApplicationProvider.getApplicationContext()
+    private lateinit var cameraProvider: ProcessCameraProvider
+    private lateinit var lifecycleOwner: FakeLifecycleOwner
+    private lateinit var cameraInfo: CameraInfo
+    private lateinit var camera: Camera
+
+    @Before
+    fun setUp() {
+        Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(cameraSelector.lensFacing!!))
+
+        // Skip test for b/168175357
+        Assume.assumeFalse(
+            "Cuttlefish has MediaCodec dequeueInput/Output buffer fails issue. Unable to test.",
+            Build.MODEL.contains("Cuttlefish") && Build.VERSION.SDK_INT == 29
+        )
+
+        cameraProvider = ProcessCameraProvider.getInstance(context).get()
+        lifecycleOwner = FakeLifecycleOwner()
+        lifecycleOwner.startAndResume()
+
+        instrumentation.runOnMainSync {
+
+            // Retrieves the target testing camera and camera info
+            camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector)
+            cameraInfo = camera.cameraInfo
+        }
+
+        // Ignore the unsupported Quality options
+        Assume.assumeTrue(
+            "Camera ${cameraSelector.lensFacing} not support $quality, skip this test item.",
+            QualitySelector.isQualitySupported(cameraInfo, quality)
+        )
+    }
+
+    @After
+    fun tearDown() {
+        if (this::cameraProvider.isInitialized) {
+            instrumentation.runOnMainSync {
+                cameraProvider.unbindAll()
+            }
+            cameraProvider.shutdown()[10, TimeUnit.SECONDS]
+        }
+    }
+
+    @Test
+    fun qualityOptionCanRecordVideo() {
+        // Arrange.
+        val recorder = Recorder.Builder().setQualitySelector(QualitySelector.from(quality)).build()
+        val videoCapture = VideoCapture.withOutput(recorder)
+        val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
+        val latchForVideoRecording = CountDownLatch(5)
+        val eventListener = Consumer<VideoRecordEvent> {
+            when (it) {
+                is VideoRecordEvent.Status -> {
+                    // Make sure the recording proceed for a while.
+                    latchForVideoRecording.countDown()
+                }
+                else -> {
+                    // Ignore other events.
+                }
+            }
+        }
+
+        instrumentation.runOnMainSync {
+            cameraProvider.bindToLifecycle(
+                lifecycleOwner,
+                cameraSelector,
+                videoCapture,
+            )
+        }
+
+        // Act.
+        videoCapture.startVideoRecording(file, eventListener).use {
+
+            // Verify the recording proceed for a while.
+            Truth.assertThat(
+                latchForVideoRecording.await(
+                    VIDEO_TIMEOUT_SEC,
+                    TimeUnit.SECONDS
+                )
+            ).isTrue()
+        }
+
+        // Clean up
+        file.delete()
+    }
+
+    private fun VideoCapture<Recorder>.startVideoRecording(
+        file: File,
+        eventListener: Consumer<VideoRecordEvent>
+    ): Recording =
+        output.prepareRecording(
+            context, FileOutputOptions.Builder(file).build()
+        ).start(
+            CameraXExecutors.directExecutor(), eventListener
+        )
+}
\ No newline at end of file
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapabilities.java b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapabilities.java
index 4ed6cdd..eb7a9f3 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapabilities.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapabilities.java
@@ -29,6 +29,7 @@
 import androidx.camera.core.impl.CamcorderProfileProxy;
 import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.impl.utils.CompareSizesByArea;
+import androidx.camera.video.internal.compat.quirk.CamcorderProfileResolutionNotSupportedByEncoderQuirk;
 import androidx.camera.video.internal.compat.quirk.DeviceQuirks;
 import androidx.camera.video.internal.compat.quirk.VideoQualityNotSupportQuirk;
 import androidx.core.util.Preconditions;
@@ -199,7 +200,14 @@
 
     private boolean isDeviceValidQuality(@NonNull Quality quality) {
         VideoQualityNotSupportQuirk quirk = DeviceQuirks.get(VideoQualityNotSupportQuirk.class);
-        return quirk == null || !quirk.isProblematicVideoQuality(quality);
+        boolean validQuality = quirk == null || !quirk.isProblematicVideoQuality(quality);
+
+        CamcorderProfileResolutionNotSupportedByEncoderQuirk quirk1 = DeviceQuirks.get(
+                CamcorderProfileResolutionNotSupportedByEncoderQuirk.class);
+        boolean validQualityForEncoder =
+                quirk1 == null || !quirk1.isProblematicVideoQuality(quality);
+
+        return validQuality && validQualityForEncoder;
     }
 
 }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/CamcorderProfileResolutionNotSupportedByEncoderQuirk.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/CamcorderProfileResolutionNotSupportedByEncoderQuirk.java
new file mode 100644
index 0000000..649f4d9
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/CamcorderProfileResolutionNotSupportedByEncoderQuirk.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2022 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.camera.video.internal.compat.quirk;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.CamcorderProfileProvider;
+import androidx.camera.core.impl.Quirk;
+import androidx.camera.video.Quality;
+import androidx.camera.video.VideoCapabilities;
+
+/**
+ * Quirk denotes that the quality {@link VideoCapabilities} queried by
+ * {@link CamcorderProfileProvider} cannot find a proper encoder codec for video recording on
+ * device.
+ *
+ * <p>The video AVC encoder on Redmi note 4 and LG K10 LTE K430 aligned down the video height.
+ * The maximum supported encoder resolution is 1920x1072 and the FHD quality option cannot find a
+ * proper encoder. See b/216583006 for more information.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class CamcorderProfileResolutionNotSupportedByEncoderQuirk implements Quirk {
+
+    // TODO(b/191678894): Update this Quirk after we can crop the output to the supported codec
+    //  resolution.
+
+    static boolean load() {
+        return isRedmiNote4() || isLGK430();
+    }
+
+    private static boolean isRedmiNote4() {
+        return "Xiaomi".equalsIgnoreCase(Build.BRAND) && "redmi note 4".equalsIgnoreCase(
+                Build.MODEL);
+    }
+
+    private static boolean isLGK430() {
+        return "lge".equalsIgnoreCase(Build.BRAND) && "lg-k430".equalsIgnoreCase(Build.MODEL);
+    }
+
+    /** Checks if the given Quality type is a problematic quality. */
+    public boolean isProblematicVideoQuality(@NonNull Quality quality) {
+        if (isRedmiNote4() || isLGK430()) {
+            return quality == Quality.FHD;
+        }
+        return false;
+    }
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java
index 08ba92e..d4940f3 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java
@@ -59,6 +59,9 @@
         if (EncoderNotUsePersistentInputSurfaceQuirk.load()) {
             quirks.add(new EncoderNotUsePersistentInputSurfaceQuirk());
         }
+        if (CamcorderProfileResolutionNotSupportedByEncoderQuirk.load()) {
+            quirks.add(new CamcorderProfileResolutionNotSupportedByEncoderQuirk());
+        }
 
         return quirks;
     }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/workaround/EncoderFinder.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/workaround/EncoderFinder.java
index 32525c0..1c1c7fe 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/workaround/EncoderFinder.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/workaround/EncoderFinder.java
@@ -17,6 +17,7 @@
 package androidx.camera.video.internal.workaround;
 
 import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaFormat;
 import android.os.Build;
@@ -24,11 +25,13 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.camera.core.Logger;
 import androidx.camera.video.internal.DebugUtils;
 import androidx.camera.video.internal.compat.quirk.DeviceQuirks;
 import androidx.camera.video.internal.compat.quirk.ExcludeKeyFrameRateInFindEncoderQuirk;
 import androidx.camera.video.internal.compat.quirk.MediaCodecInfoReportIncorrectInfoQuirk;
 import androidx.camera.video.internal.encoder.InvalidConfigException;
+import androidx.core.util.Preconditions;
 
 import java.io.IOException;
 
@@ -42,6 +45,8 @@
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class EncoderFinder {
+    private static final String TAG = "EncoderFinder";
+
     private final boolean mShouldRemoveKeyFrameRate;
 
     public EncoderFinder() {
@@ -110,7 +115,12 @@
                 mediaFormat.setString(MediaFormat.KEY_AAC_PROFILE, null);
             }
 
-            return mediaCodecList.findEncoderForFormat(mediaFormat);
+            String name = mediaCodecList.findEncoderForFormat(mediaFormat);
+            if (name == null) {
+                name = findEncoderWithNearestCompatibleBitrate(mediaFormat,
+                        mediaCodecList.getCodecInfos());
+            }
+            return name;
         } finally {
             // Restore the frame rate value.
             if (tempFrameRate != null) {
@@ -124,6 +134,52 @@
         }
     }
 
+    @Nullable
+    private String findEncoderWithNearestCompatibleBitrate(@NonNull MediaFormat mediaFormat,
+            @NonNull MediaCodecInfo[] codecInfoList) {
+        String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
+        if (mime == null) {
+            Logger.w(TAG, "MediaFormat does not contain mime info.");
+            return null;
+        }
+
+        for (MediaCodecInfo info : codecInfoList) {
+            if (!info.isEncoder()) {
+                continue;
+            }
+            Integer origBitrate = null;
+            try {
+                MediaCodecInfo.CodecCapabilities caps = info.getCapabilitiesForType(mime);
+                Preconditions.checkArgument(caps != null, "MIME type is not supported");
+
+                int newBitrate = -1;
+                if (mediaFormat.containsKey(MediaFormat.KEY_BIT_RATE)) {
+
+                    // We only handle video bitrate issues at this moment.
+                    MediaCodecInfo.VideoCapabilities videoCaps = caps.getVideoCapabilities();
+                    Preconditions.checkArgument(videoCaps != null, "Not video codec");
+
+                    origBitrate = mediaFormat.getInteger(MediaFormat.KEY_BIT_RATE);
+                    newBitrate =  videoCaps.getBitrateRange().clamp(origBitrate);
+                    mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, newBitrate);
+                }
+                if (caps.isFormatSupported(mediaFormat)) {
+                    Logger.w(TAG, String.format("No encoder found that supports requested bitrate"
+                            + ". Adjusting bitrate to nearest supported bitrate [requested: "
+                            + "%dbps, nearest: %dbps]", origBitrate, newBitrate));
+                    return info.getName();
+                }
+            } catch (IllegalArgumentException e) {
+                // Not supported case.
+            } finally {
+                if (origBitrate != null) {
+                    mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, origBitrate);
+                }
+            }
+        }
+        return null;
+    }
+
     private boolean shouldCreateCodecByType(@NonNull MediaFormat mediaFormat) {
         MediaCodecInfoReportIncorrectInfoQuirk quirk =
                 DeviceQuirks.get(MediaCodecInfoReportIncorrectInfoQuirk.class);
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/RoutingDemoModels.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/RoutingDemoModels.java
index 248a8ea..71c7c36 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/RoutingDemoModels.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/RoutingDemoModels.java
@@ -23,8 +23,10 @@
 import android.text.Spanned;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.OptIn;
 import androidx.car.app.CarContext;
 import androidx.car.app.CarToast;
+import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.model.Action;
 import androidx.car.app.model.ActionStrip;
 import androidx.car.app.model.CarColor;
@@ -125,6 +127,7 @@
      * Returns the action strip that contains a "bug report" button and "stop navigation" button.
      */
     @NonNull
+    @OptIn(markerClass = ExperimentalCarApi.class)
     public static ActionStrip getActionStrip(
             @NonNull CarContext carContext, @NonNull OnClickListener onStopNavigation) {
         return new ActionStrip.Builder()
@@ -147,6 +150,7 @@
                         new Action.Builder()
                                 .setTitle("Stop")
                                 .setOnClickListener(onStopNavigation)
+                                .setFlags(Action.FLAG_IS_PERSISTENT)
                                 .build())
                 .build();
     }
diff --git a/car/app/app/api/public_plus_experimental_1.2.0-beta03.txt b/car/app/app/api/public_plus_experimental_1.2.0-beta03.txt
index bcf427c..c2b190a8 100644
--- a/car/app/app/api/public_plus_experimental_1.2.0-beta03.txt
+++ b/car/app/app/api/public_plus_experimental_1.2.0-beta03.txt
@@ -453,6 +453,7 @@
     method public static String typeToString(int);
     field public static final androidx.car.app.model.Action APP_ICON;
     field public static final androidx.car.app.model.Action BACK;
+    field @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public static final int FLAG_IS_PERSISTENT = 2; // 0x2
     field @androidx.car.app.annotations.RequiresCarApi(4) public static final int FLAG_PRIMARY = 1; // 0x1
     field public static final androidx.car.app.model.Action PAN;
     field public static final int TYPE_APP_ICON = 65538; // 0x10002
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index bcf427c..c2b190a8 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -453,6 +453,7 @@
     method public static String typeToString(int);
     field public static final androidx.car.app.model.Action APP_ICON;
     field public static final androidx.car.app.model.Action BACK;
+    field @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public static final int FLAG_IS_PERSISTENT = 2; // 0x2
     field @androidx.car.app.annotations.RequiresCarApi(4) public static final int FLAG_PRIMARY = 1; // 0x1
     field public static final androidx.car.app.model.Action PAN;
     field public static final int TYPE_APP_ICON = 65538; // 0x10002
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Action.java b/car/app/app/src/main/java/androidx/car/app/model/Action.java
index 832ac5e..6f3b005 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Action.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Action.java
@@ -32,9 +32,11 @@
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
 import androidx.annotation.RestrictTo;
 import androidx.car.app.CarContext;
 import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.constraints.CarIconConstraints;
 import androidx.lifecycle.LifecycleOwner;
@@ -86,10 +88,12 @@
      * @hide
      */
     @RestrictTo(LIBRARY)
+    @OptIn(markerClass = ExperimentalCarApi.class)
     @IntDef(
             flag = true,
             value = {
                     FLAG_PRIMARY,
+                    FLAG_IS_PERSISTENT,
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ActionFlag {
@@ -133,6 +137,13 @@
     public static final int FLAG_PRIMARY = 1 << 0;
 
     /**
+     * Indicates that this action will not fade in/out inside an {@link ActionStrip}.
+     */
+    @RequiresCarApi(5)
+    @ExperimentalCarApi
+    public static final int FLAG_IS_PERSISTENT = 1 << 1;
+
+    /**
      * A standard action to show the app's icon.
      *
      * <p>This action is non-interactive.
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
index a65ddea..f3d112d 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
@@ -69,6 +69,7 @@
             6000 to "1.2.0-alpha01",
             6100 to "1.2.0-alpha02",
             6200 to "1.2.0-alpha03",
+            6300 to "1.2.0-alpha04",
         )
 
         /**
@@ -81,7 +82,7 @@
          * The maven version string of this compiler. This string should be updated before/after every
          * release.
          */
-        const val compilerVersion: String = "1.2.0-alpha03"
+        const val compilerVersion: String = "1.2.0-alpha04"
         private val minimumRuntimeVersion: String
             get() = versionTable[minimumRuntimeVersionInt] ?: "unknown"
     }
diff --git a/compose/foundation/foundation-layout/api/current.txt b/compose/foundation/foundation-layout/api/current.txt
index a76c2c1..0e4b541 100644
--- a/compose/foundation/foundation-layout/api/current.txt
+++ b/compose/foundation/foundation-layout/api/current.txt
@@ -206,5 +206,67 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Spacer(androidx.compose.ui.Modifier modifier);
   }
 
+  @androidx.compose.runtime.Stable public interface WindowInsets {
+    method public int getBottom(androidx.compose.ui.unit.Density density);
+    method public int getLeft(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+    method public int getRight(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+    method public int getTop(androidx.compose.ui.unit.Density density);
+    field public static final androidx.compose.foundation.layout.WindowInsets.Companion Companion;
+  }
+
+  public static final class WindowInsets.Companion {
+  }
+
+  public final class WindowInsetsKt {
+    method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional int left, optional int top, optional int right, optional int bottom);
+    method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional float left, optional float top, optional float right, optional float bottom);
+    method public static androidx.compose.foundation.layout.WindowInsets add(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Composable public static androidx.compose.foundation.layout.PaddingValues asPaddingValues(androidx.compose.foundation.layout.WindowInsets);
+    method public static androidx.compose.foundation.layout.WindowInsets exclude(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+    method public static androidx.compose.foundation.layout.WindowInsets union(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+  }
+
+  public final class WindowInsetsPaddingKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsPadding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+  }
+
+  public final class WindowInsetsPadding_androidKt {
+    method public static androidx.compose.ui.Modifier captionBarPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier displayCutoutPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier imePadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier mandatorySystemGesturesPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier navigationBarsPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier safeContentPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier safeDrawingPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier safeGesturesPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier statusBarsPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier systemBarsPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier systemGesturesPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier waterfallPadding(androidx.compose.ui.Modifier);
+  }
+
+  public final class WindowInsetsSizeKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsBottomHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsEndWidth(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsStartWidth(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsTopHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+  }
+
+  public final class WindowInsets_androidKt {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBar(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getDisplayCutout(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getIme(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getMandatorySystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getNavigationBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeContent(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeDrawing(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getStatusBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getTappableElement(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getWaterfall(androidx.compose.foundation.layout.WindowInsets.Companion);
+  }
+
 }
 
diff --git a/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt b/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
index 11582d2..359b8d2 100644
--- a/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
@@ -209,5 +209,69 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Spacer(androidx.compose.ui.Modifier modifier);
   }
 
+  @androidx.compose.runtime.Stable public interface WindowInsets {
+    method public int getBottom(androidx.compose.ui.unit.Density density);
+    method public int getLeft(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+    method public int getRight(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+    method public int getTop(androidx.compose.ui.unit.Density density);
+    field public static final androidx.compose.foundation.layout.WindowInsets.Companion Companion;
+  }
+
+  public static final class WindowInsets.Companion {
+  }
+
+  public final class WindowInsetsKt {
+    method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional int left, optional int top, optional int right, optional int bottom);
+    method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional float left, optional float top, optional float right, optional float bottom);
+    method public static androidx.compose.foundation.layout.WindowInsets add(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Composable public static androidx.compose.foundation.layout.PaddingValues asPaddingValues(androidx.compose.foundation.layout.WindowInsets);
+    method public static androidx.compose.foundation.layout.WindowInsets exclude(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+    method public static androidx.compose.foundation.layout.WindowInsets union(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+  }
+
+  public final class WindowInsetsPaddingKt {
+    method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumedWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumedWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues paddingValues);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsPadding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+  }
+
+  public final class WindowInsetsPadding_androidKt {
+    method public static androidx.compose.ui.Modifier captionBarPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier displayCutoutPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier imePadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier mandatorySystemGesturesPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier navigationBarsPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier safeContentPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier safeDrawingPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier safeGesturesPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier statusBarsPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier systemBarsPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier systemGesturesPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier waterfallPadding(androidx.compose.ui.Modifier);
+  }
+
+  public final class WindowInsetsSizeKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsBottomHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsEndWidth(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsStartWidth(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsTopHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+  }
+
+  public final class WindowInsets_androidKt {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBar(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getDisplayCutout(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getIme(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getMandatorySystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getNavigationBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeContent(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeDrawing(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getStatusBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getTappableElement(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getWaterfall(androidx.compose.foundation.layout.WindowInsets.Companion);
+  }
+
 }
 
diff --git a/compose/foundation/foundation-layout/api/restricted_current.txt b/compose/foundation/foundation-layout/api/restricted_current.txt
index 9355fc6..6dc4d15 100644
--- a/compose/foundation/foundation-layout/api/restricted_current.txt
+++ b/compose/foundation/foundation-layout/api/restricted_current.txt
@@ -211,5 +211,67 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Spacer(androidx.compose.ui.Modifier modifier);
   }
 
+  @androidx.compose.runtime.Stable public interface WindowInsets {
+    method public int getBottom(androidx.compose.ui.unit.Density density);
+    method public int getLeft(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+    method public int getRight(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+    method public int getTop(androidx.compose.ui.unit.Density density);
+    field public static final androidx.compose.foundation.layout.WindowInsets.Companion Companion;
+  }
+
+  public static final class WindowInsets.Companion {
+  }
+
+  public final class WindowInsetsKt {
+    method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional int left, optional int top, optional int right, optional int bottom);
+    method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional float left, optional float top, optional float right, optional float bottom);
+    method public static androidx.compose.foundation.layout.WindowInsets add(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Composable public static androidx.compose.foundation.layout.PaddingValues asPaddingValues(androidx.compose.foundation.layout.WindowInsets);
+    method public static androidx.compose.foundation.layout.WindowInsets exclude(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+    method public static androidx.compose.foundation.layout.WindowInsets union(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+  }
+
+  public final class WindowInsetsPaddingKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsPadding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+  }
+
+  public final class WindowInsetsPadding_androidKt {
+    method public static androidx.compose.ui.Modifier captionBarPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier displayCutoutPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier imePadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier mandatorySystemGesturesPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier navigationBarsPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier safeContentPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier safeDrawingPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier safeGesturesPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier statusBarsPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier systemBarsPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier systemGesturesPadding(androidx.compose.ui.Modifier);
+    method public static androidx.compose.ui.Modifier waterfallPadding(androidx.compose.ui.Modifier);
+  }
+
+  public final class WindowInsetsSizeKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsBottomHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsEndWidth(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsStartWidth(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsTopHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+  }
+
+  public final class WindowInsets_androidKt {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBar(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getDisplayCutout(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getIme(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getMandatorySystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getNavigationBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeContent(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeDrawing(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getStatusBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getTappableElement(androidx.compose.foundation.layout.WindowInsets.Companion);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getWaterfall(androidx.compose.foundation.layout.WindowInsets.Companion);
+  }
+
 }
 
diff --git a/compose/foundation/foundation-layout/build.gradle b/compose/foundation/foundation-layout/build.gradle
index 45de895..7a0b162 100644
--- a/compose/foundation/foundation-layout/build.gradle
+++ b/compose/foundation/foundation-layout/build.gradle
@@ -36,11 +36,12 @@
          */
 
         api("androidx.annotation:annotation:1.1.0")
-        api("androidx.compose.ui:ui:1.1.0-rc01")
+        api(project(":compose:ui:ui"))
         api("androidx.compose.ui:ui-unit:1.1.0-rc01")
 
         implementation("androidx.compose.runtime:runtime:1.1.0-rc01")
         implementation("androidx.compose.ui:ui-util:1.0.0")
+        implementation("androidx.core:core:1.7.0")
         implementation(libs.kotlinStdlibCommon)
 
         testImplementation(libs.testRules)
@@ -88,6 +89,7 @@
 
             androidMain.dependencies {
                 api("androidx.annotation:annotation:1.1.0")
+                implementation("androidx.core:core:1.7.0")
             }
 
             desktopMain.dependencies {
diff --git a/compose/foundation/foundation-layout/samples/build.gradle b/compose/foundation/foundation-layout/samples/build.gradle
index 2fcf57d..02cf445 100644
--- a/compose/foundation/foundation-layout/samples/build.gradle
+++ b/compose/foundation/foundation-layout/samples/build.gradle
@@ -36,6 +36,8 @@
     implementation(project(":compose:runtime:runtime"))
     implementation("androidx.compose.ui:ui:1.0.0")
     implementation("androidx.compose.ui:ui-text:1.0.0")
+    implementation("androidx.core:core-ktx:1.7.0")
+    implementation("androidx.activity:activity-compose:1.4.0")
 }
 
 androidx {
diff --git a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/WindowInsetsPaddingSample.kt b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/WindowInsetsPaddingSample.kt
new file mode 100644
index 0000000..0b323e8
--- /dev/null
+++ b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/WindowInsetsPaddingSample.kt
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2022 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.foundation.layout.samples
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.asPaddingValues
+import androidx.compose.foundation.layout.captionBarPadding
+import androidx.compose.foundation.layout.consumedWindowInsets
+import androidx.compose.foundation.layout.displayCutoutPadding
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.ime
+import androidx.compose.foundation.layout.imePadding
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.layout.mandatorySystemGesturesPadding
+import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeContentPadding
+import androidx.compose.foundation.layout.safeDrawingPadding
+import androidx.compose.foundation.layout.safeGesturesPadding
+import androidx.compose.foundation.layout.statusBars
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.layout.systemBars
+import androidx.compose.foundation.layout.systemBarsPadding
+import androidx.compose.foundation.layout.systemGesturesPadding
+import androidx.compose.foundation.layout.union
+import androidx.compose.foundation.layout.waterfallPadding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.dp
+import androidx.core.view.WindowCompat
+
+@Sampled
+fun captionBarPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.captionBarPadding()) {
+                    // app content
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun systemBarsPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.systemBarsPadding()) {
+                    // app content
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun displayCutoutPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.background(Color.Blue).statusBarsPadding()) {
+                    Box(Modifier.background(Color.Yellow).displayCutoutPadding()) {
+                        // app content
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun statusBarsAndNavigationBarsPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.background(Color.Blue).statusBarsPadding()) {
+                    Box(Modifier.background(Color.Green).navigationBarsPadding()) {
+                        // app content
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun imePaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.background(Color.Blue).systemBarsPadding()) {
+                    Box(Modifier.imePadding()) {
+                        // app content
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun waterfallPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.background(Color.Blue).systemBarsPadding()) {
+                    // The app content shouldn't spill over the edges. They will be green.
+                    Box(Modifier.background(Color.Green).waterfallPadding()) {
+                        // app content
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun systemGesturesPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.background(Color.Blue).systemBarsPadding()) {
+                    // The app content won't interfere with the system gestures area.
+                    // It will just be white.
+                    Box(Modifier.background(Color.White).systemGesturesPadding()) {
+                        // app content
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun mandatorySystemGesturesPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.background(Color.Blue).systemBarsPadding()) {
+                    // The app content won't interfere with the mandatory system gestures area.
+                    // It will just be white.
+                    Box(Modifier.background(Color.White).mandatorySystemGesturesPadding()) {
+                        // app content
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun safeDrawingPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.background(Color.Black).systemBarsPadding()) {
+                    // The app content won't have anything drawing over it, but all the
+                    // background not in the status bars will be white.
+                    Box(Modifier.background(Color.White).safeDrawingPadding()) {
+                        // app content
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun safeGesturesPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.background(Color.Black).systemBarsPadding()) {
+                    // The app content will only be drawn where there is no possible
+                    // gesture confusion. The rest will be plain white
+                    Box(Modifier.background(Color.White).safeGesturesPadding()) {
+                        // app content
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun safeContentPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.background(Color.Black).systemBarsPadding()) {
+                    // The app content will only be drawn where there is no possible
+                    // gesture confusion and content will not be drawn over.
+                    // The rest will be plain white
+                    Box(Modifier.background(Color.White).safeContentPadding()) {
+                        // app content
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun insetsPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                val insets = WindowInsets.systemBars.union(WindowInsets.ime)
+                Box(Modifier.background(Color.White).fillMaxSize().windowInsetsPadding(insets)) {
+                    // app content
+                }
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalLayoutApi::class)
+@Sampled
+fun consumedInsetsPaddingSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                with(LocalDensity.current) {
+                    val paddingValues = PaddingValues(horizontal = 20.dp)
+                    Box(
+                        Modifier
+                            .padding(paddingValues)
+                            .consumedWindowInsets(paddingValues)
+                    ) {
+                        // app content
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun insetsInt() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                // Make sure we are at least 10 pixels away from the top.
+                val insets = WindowInsets.statusBars.union(WindowInsets(top = 10))
+                Box(Modifier.windowInsetsPadding(insets)) {
+                    // app content
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun insetsDp() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                // Make sure we are at least 10 DP away from the top.
+                val insets = WindowInsets.statusBars.union(WindowInsets(top = 10.dp))
+                Box(Modifier.windowInsetsPadding(insets)) {
+                    // app content
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun paddingValuesSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                LazyColumn(
+                    contentPadding = WindowInsets.navigationBars.asPaddingValues()
+                ) {
+                    // items
+                }
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalLayoutApi::class)
+@Sampled
+fun consumedInsetsSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.padding(WindowInsets.navigationBars.asPaddingValues())) {
+                    Box(Modifier.consumedWindowInsets(WindowInsets.navigationBars)) {
+                        // app content
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/WindowInsetsSizeSample.kt b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/WindowInsetsSizeSample.kt
new file mode 100644
index 0000000..6b875a8
--- /dev/null
+++ b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/WindowInsetsSizeSample.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2022 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.foundation.layout.samples
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.statusBars
+import androidx.compose.foundation.layout.windowInsetsEndWidth
+import androidx.compose.foundation.layout.windowInsetsStartWidth
+import androidx.compose.foundation.layout.windowInsetsTopHeight
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.core.view.WindowCompat
+
+@Sampled
+fun insetsStartWidthSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.fillMaxSize()) {
+                    // Background for navigation bar at the start
+                    Box(Modifier.windowInsetsStartWidth(WindowInsets.navigationBars)
+                        .fillMaxHeight()
+                        .align(Alignment.CenterStart)
+                        .background(Color.Red)
+                    )
+                    // app content
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun insetsTopHeightSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.fillMaxSize()) {
+                    // Background for status bar at the top
+                    Box(Modifier.windowInsetsTopHeight(WindowInsets.statusBars)
+                        .fillMaxWidth()
+                        .align(Alignment.TopCenter)
+                        .background(Color.Red)
+                    )
+                    // app content
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun insetsEndWidthSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.fillMaxSize()) {
+                    // Background for navigation bar at the end
+                    Box(Modifier.windowInsetsEndWidth(WindowInsets.navigationBars)
+                        .fillMaxHeight()
+                        .align(Alignment.CenterEnd)
+                        .background(Color.Red)
+                    )
+                    // app content
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+fun insetsBottomHeightSample() {
+    class SampleActivity : ComponentActivity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            WindowCompat.setDecorFitsSystemWindows(window, false)
+            super.onCreate(savedInstanceState)
+            setContent {
+                Box(Modifier.fillMaxSize()) {
+                    // Background for navigation bar at the bottom
+                    Box(Modifier.windowInsetsTopHeight(WindowInsets.navigationBars)
+                        .fillMaxWidth()
+                        .align(Alignment.BottomCenter)
+                        .background(Color.Red)
+                    )
+                    // app content
+                }
+            }
+        }
+    }
+}
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsPaddingTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsPaddingTest.kt
new file mode 100644
index 0000000..02fc73a
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsPaddingTest.kt
@@ -0,0 +1,872 @@
+/*
+ * Copyright 2022 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.foundation.layout
+
+import android.graphics.Insets as FrameworkInsets
+import android.graphics.Rect as AndroidRect
+import android.view.WindowInsets as AndroidWindowInsets
+import android.content.Context
+import android.os.Build
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsetsAnimation
+import android.view.animation.LinearInterpolator
+import android.widget.FrameLayout
+import androidx.activity.ComponentActivity
+import androidx.annotation.RequiresApi
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.boundsInRoot
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.ViewRootForTest
+import androidx.compose.ui.test.junit4.AndroidComposeTestRule
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.round
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.graphics.Insets as AndroidXInsets
+import androidx.core.view.DisplayCutoutCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.forEach
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class WindowInsetsPaddingTest {
+    @get:Rule
+    val rule = createAndroidComposeRule<ComponentActivity>()
+
+    private lateinit var insetsView: InsetsView
+
+    @Before
+    fun setup() {
+        WindowInsetsHolder.setUseTestInsets(true)
+    }
+
+    @After
+    fun teardown() {
+        WindowInsetsHolder.setUseTestInsets(false)
+    }
+
+    @Test
+    fun systemBarsPadding() {
+        testInsetsPadding(
+            WindowInsetsCompat.Type.systemBars(),
+            Modifier.systemBarsPadding()
+        )
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun displayCutoutPadding() {
+        val coordinates = setInsetContent {
+            Modifier.displayCutoutPadding()
+        }
+
+        val (width, height) = rule.runOnIdle {
+            coordinates.boundsInRoot().bottomRight.round()
+        }
+
+        val insets = sendDisplayCutoutInsets(width, height)
+        insets.assertIsConsumed(WindowInsetsCompat.Type.displayCutout())
+
+        rule.runOnIdle {
+            val expectedRect = Rect(10f, 11f, width - 12f, height - 13f)
+            assertThat(coordinates.boundsInRoot()).isEqualTo(expectedRect)
+        }
+    }
+
+    private fun sendDisplayCutoutInsets(width: Int, height: Int): WindowInsetsCompat {
+        val centerWidth = width / 2
+        val centerHeight = height / 2
+
+        val left = AndroidRect(0, centerHeight, 10, centerHeight + 2)
+        val top = AndroidRect(centerWidth, 0, centerWidth + 2, 11)
+        val right = AndroidRect(width - 12, centerHeight, width, centerHeight + 2)
+        val bottom = AndroidRect(centerWidth, height - 13, centerWidth + 2, height)
+        val safeInsets = AndroidXInsets.of(10, 11, 12, 13)
+        val windowInsets = WindowInsetsCompat.Builder()
+            .setInsets(WindowInsetsCompat.Type.statusBars(), AndroidXInsets.of(0, 11, 0, 0))
+            .setInsets(WindowInsetsCompat.Type.displayCutout(), safeInsets)
+            .setDisplayCutout(
+                DisplayCutoutCompat(
+                    safeInsets,
+                    left,
+                    top,
+                    right,
+                    bottom,
+                    AndroidXInsets.of(1, 2, 3, 4)
+                )
+            )
+            .build()
+        return dispatchApplyWindowInsets(windowInsets)
+    }
+
+    @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun statusBarsPaddingApi21() {
+        testInsetsPadding(
+            WindowInsetsCompat.Type.statusBars(),
+            Modifier.statusBarsPadding()
+        ) { width, height ->
+            Rect(0f, 11f, width.toFloat(), height.toFloat())
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun statusBarsPaddingApi30() {
+        testInsetsPadding(
+            WindowInsetsCompat.Type.statusBars(),
+            Modifier.statusBarsPadding()
+        )
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun captionBarPadding() {
+        testInsetsPadding(
+            WindowInsetsCompat.Type.captionBar(),
+            Modifier.captionBarPadding()
+        )
+    }
+
+    @Test
+    fun navigationBarsPaddingLeft() {
+        testInsetsPadding(
+            WindowInsetsCompat.Type.navigationBars(),
+            Modifier.navigationBarsPadding(),
+            sentInsets = AndroidXInsets.of(10, 0, 0, 0)
+        ) { width, height ->
+            Rect(10f, 0f, width.toFloat(), height.toFloat())
+        }
+    }
+
+    @Test
+    fun navigationBarsPaddingRight() {
+        testInsetsPadding(
+            WindowInsetsCompat.Type.navigationBars(),
+            Modifier.navigationBarsPadding(),
+            sentInsets = AndroidXInsets.of(0, 0, 12, 0)
+        ) { width, height ->
+            Rect(0f, 0f, width - 12f, height.toFloat())
+        }
+    }
+
+    @Test
+    fun navigationBarsPaddingBottom() {
+        testInsetsPadding(
+            WindowInsetsCompat.Type.navigationBars(),
+            Modifier.navigationBarsPadding(),
+            sentInsets = AndroidXInsets.of(0, 0, 0, 13)
+        ) { width, height ->
+            Rect(0f, 0f, width.toFloat(), height - 13f)
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun navigationBarsPaddingApi30() {
+        testInsetsPadding(
+            WindowInsetsCompat.Type.navigationBars(),
+            Modifier.navigationBarsPadding()
+        )
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun insetsPaddingIme() = testInsetsPadding(WindowInsetsCompat.Type.ime()) {
+        Modifier.windowInsetsPadding(WindowInsets.ime)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun insetsPaddingDisplayCutout() = testInsetsPadding(WindowInsetsCompat.Type.displayCutout()) {
+        Modifier.windowInsetsPadding(WindowInsets.displayCutout)
+    }
+
+    @Test
+    fun insetsPaddingStatusBarsTop() = testInsetsPadding(
+        WindowInsetsCompat.Type.statusBars(),
+        sentInsets = AndroidXInsets.of(0, 10, 0, 0),
+        expected = { w, h -> Rect(0f, 10f, w.toFloat(), h.toFloat()) }
+    ) { Modifier.windowInsetsPadding(WindowInsets.statusBars) }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun insetsPaddingStatusBarsApi30() = testInsetsPadding(WindowInsetsCompat.Type.statusBars()) {
+        Modifier.windowInsetsPadding(WindowInsets.statusBars)
+    }
+
+    @Test
+    fun insetsPaddingSystemBars() = testInsetsPadding(WindowInsetsCompat.Type.systemBars()) {
+        Modifier.windowInsetsPadding(WindowInsets.systemBars)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun insetsPaddingTappableElement() =
+        testInsetsPadding(WindowInsetsCompat.Type.tappableElement()) {
+            Modifier.windowInsetsPadding(WindowInsets.tappableElement)
+        }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun insetsPaddingCaptionBar() = testInsetsPadding(WindowInsetsCompat.Type.captionBar()) {
+        Modifier.windowInsetsPadding(WindowInsets.captionBar)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun insetsPaddingMandatorySystemGestures() =
+        testInsetsPadding(WindowInsetsCompat.Type.mandatorySystemGestures()) {
+            Modifier.windowInsetsPadding(WindowInsets.mandatorySystemGestures)
+        }
+
+    @Test
+    fun insetsPaddingNavigationBarsLeft() =
+        testInsetsPadding(
+            WindowInsetsCompat.Type.navigationBars(),
+            sentInsets = AndroidXInsets.of(10, 0, 0, 0),
+            expected = { width, height -> Rect(10f, 0f, width.toFloat(), height.toFloat()) }
+        ) {
+            Modifier.windowInsetsPadding(WindowInsets.navigationBars)
+        }
+
+    @Test
+    fun insetsPaddingNavigationBarsRight() =
+        testInsetsPadding(
+            WindowInsetsCompat.Type.navigationBars(),
+            sentInsets = AndroidXInsets.of(0, 0, 10, 0),
+            expected = { width, height -> Rect(0f, 0f, width - 10f, height.toFloat()) }
+        ) {
+            Modifier.windowInsetsPadding(WindowInsets.navigationBars)
+        }
+
+    @Test
+    fun insetsPaddingNavigationBarsBottom() =
+        testInsetsPadding(
+            WindowInsetsCompat.Type.navigationBars(),
+            sentInsets = AndroidXInsets.of(0, 0, 0, 10),
+            expected = { width, height -> Rect(0f, 0f, width.toFloat(), height - 10f) }
+        ) {
+            Modifier.windowInsetsPadding(WindowInsets.navigationBars)
+        }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun insetsPaddingNavigationBarsApi30() =
+        testInsetsPadding(WindowInsetsCompat.Type.navigationBars()) {
+            Modifier.windowInsetsPadding(WindowInsets.navigationBars)
+        }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun insetsPaddingWaterfall() {
+        val coordinates = setInsetContent {
+            Modifier.windowInsetsPadding(WindowInsets.waterfall)
+        }
+
+        val (width, height) = rule.runOnIdle {
+            coordinates.boundsInRoot().bottomRight.round()
+        }
+
+        val insets = sendDisplayCutoutInsets(width, height)
+        insets.assertIsConsumed(WindowInsetsCompat.Type.displayCutout())
+
+        rule.runOnIdle {
+            val expectedRect = Rect(1f, 2f, width - 3f, height - 4f)
+            assertThat(coordinates.boundsInRoot()).isEqualTo(expectedRect)
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun insetsPaddingSystemGestures() =
+        testInsetsPadding(WindowInsetsCompat.Type.systemGestures()) {
+            Modifier.windowInsetsPadding(WindowInsets.systemGestures)
+        }
+
+    @Test
+    fun mixedInsetsPadding() {
+        val coordinates = setInsetContent {
+            val windowInsets = WindowInsets
+            val insets =
+                windowInsets.navigationBars.union(windowInsets.statusBars).union(windowInsets.ime)
+            Modifier.windowInsetsPadding(insets)
+        }
+
+        val insets = WindowInsetsCompat.Builder()
+            .setInsets(WindowInsetsCompat.Type.navigationBars(), AndroidXInsets.of(0, 0, 0, 15))
+            .setInsets(WindowInsetsCompat.Type.statusBars(), AndroidXInsets.of(0, 10, 0, 0))
+            .setInsets(WindowInsetsCompat.Type.ime(), AndroidXInsets.of(0, 0, 0, 5))
+            .build()
+
+        dispatchApplyWindowInsets(insets)
+
+        rule.runOnIdle {
+            val view = insetsView.findComposeView()
+            val width = view.width
+            val height = view.height
+            assertThat(coordinates.boundsInRoot())
+                .isEqualTo(Rect(0f, 10f, width.toFloat(), height - 15f))
+        }
+    }
+
+    @OptIn(ExperimentalLayoutApi::class)
+    @Test
+    fun consumedInsets() {
+        lateinit var coordinates: LayoutCoordinates
+
+        setContent {
+            with(LocalDensity.current) {
+                CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
+                    Box(
+                        Modifier.fillMaxSize().padding(5.toDp(), 4.toDp(), 3.toDp(), 2.toDp())
+                            .consumedWindowInsets(WindowInsets(5, 4, 3, 2))
+                    ) {
+                        Box(Modifier.fillMaxSize().systemBarsPadding()) {
+                            Box(Modifier.fillMaxSize().onGloballyPositioned { coordinates = it })
+                        }
+                    }
+                }
+            }
+        }
+
+        val insets = WindowInsetsCompat.Builder()
+            .setInsets(WindowInsetsCompat.Type.systemBars(), AndroidXInsets.of(10, 11, 12, 13))
+            .build()
+
+        dispatchApplyWindowInsets(insets)
+
+        rule.runOnIdle {
+            val view = insetsView.findComposeView()
+            val width = view.width
+            val height = view.height
+            assertThat(coordinates.boundsInRoot())
+                .isEqualTo(Rect(10f, 11f, width - 12f, height - 13f))
+        }
+    }
+
+    @Test
+    fun consumedPadding() {
+        lateinit var coordinates: LayoutCoordinates
+
+        setContent {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
+                Box(Modifier.statusBarsPadding()) {
+                    Box(Modifier.systemBarsPadding()) {
+                        Box(Modifier.fillMaxSize().onGloballyPositioned { coordinates = it })
+                    }
+                }
+            }
+        }
+
+        // wait for layout
+        rule.waitForIdle()
+
+        val insets = WindowInsetsCompat.Builder()
+            .setInsets(WindowInsetsCompat.Type.statusBars(), AndroidXInsets.of(0, 5, 0, 0))
+            .setInsets(WindowInsetsCompat.Type.systemBars(), AndroidXInsets.of(10, 11, 12, 13))
+            .build()
+
+        dispatchApplyWindowInsets(insets)
+
+        rule.runOnIdle {
+            val view = insetsView.findComposeView()
+            val width = view.width
+            val height = view.height
+            assertThat(coordinates.boundsInRoot())
+                .isEqualTo(Rect(10f, 11f, width - 12f, height - 13f))
+        }
+    }
+
+    private fun testInsetsPadding(
+        type: Int,
+        modifier: Modifier,
+        sentInsets: AndroidXInsets = AndroidXInsets.of(10, 11, 12, 13),
+        expected: (Int, Int) -> Rect = { width, height ->
+            Rect(10f, 11f, width - 12f, height - 13f)
+        }
+    ) {
+        testInsetsPadding(type, sentInsets, expected) { modifier }
+    }
+
+    private fun testInsetsPadding(
+        type: Int,
+        sentInsets: AndroidXInsets = AndroidXInsets.of(10, 11, 12, 13),
+        expected: (Int, Int) -> Rect = { width, height ->
+            Rect(10f, 11f, width - 12f, height - 13f)
+        },
+        modifier: @Composable () -> Modifier,
+    ) {
+        val coordinates = setInsetContent(modifier)
+
+        val insets = sendInsets(type, sentInsets)
+        insets.assertIsConsumed(type)
+
+        rule.runOnIdle {
+            val view = insetsView.findComposeView()
+            val width = view.width
+            val height = view.height
+            val expectedRect = expected(width, height)
+            assertThat(coordinates.boundsInRoot()).isEqualTo(expectedRect)
+        }
+    }
+
+    // Removing the last Modifier handling insets should stop insets from being consumed
+    @Test
+    fun removeLastInsetsPadding() {
+        var useStatusBarInsets by mutableStateOf(true)
+        var useNavigationBarInsets by mutableStateOf(true)
+        val coordinates = setInsetContent {
+            (if (useStatusBarInsets) Modifier.statusBarsPadding() else Modifier).then(
+                if (useNavigationBarInsets) Modifier.navigationBarsPadding() else Modifier
+            )
+        }
+
+        rule.runOnIdle {
+            useStatusBarInsets = false
+        }
+
+        sendInsets(WindowInsetsCompat.Type.systemBars())
+            .assertIsConsumed(WindowInsetsCompat.Type.systemBars())
+
+        rule.runOnIdle {
+            useNavigationBarInsets = false
+        }
+
+        sendInsets(WindowInsetsCompat.Type.systemBars())
+            .assertIsNotConsumed(WindowInsetsCompat.Type.systemBars())
+
+        rule.runOnIdle {
+            val view = insetsView.findComposeView()
+            val width = view.width.toFloat()
+            val height = view.height.toFloat()
+            assertThat(coordinates.boundsInRoot()).isEqualTo(Rect(0f, 0f, width, height))
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun animateImeInsets() {
+        with(Api30Methods(rule)) {
+            val coordinates = setInsetContent { Modifier.systemBarsPadding().imePadding() }
+
+            sendInsets(WindowInsetsCompat.Type.systemBars())
+
+            val view = insetsView.findComposeView()
+            val animation = sendImeStart(view)
+
+            val width = view.width
+            val height = view.height
+
+            animation.sendImeProgress(view, 0f)
+
+            rule.runOnIdle {
+                assertThat(coordinates.boundsInRoot())
+                    .isEqualTo(Rect(10f, 11f, width - 12f, height - 13f))
+            }
+
+            animation.sendImeProgress(view, 0.75f)
+
+            rule.runOnIdle {
+                assertThat(coordinates.boundsInRoot())
+                    .isEqualTo(Rect(10f, 11f, width - 12f, height - 15f))
+            }
+
+            animation.sendImeProgress(view, 1f)
+
+            rule.runOnIdle {
+                assertThat(coordinates.boundsInRoot())
+                    .isEqualTo(Rect(10f, 11f, width - 12f, height - 20f))
+            }
+
+            animation.sendImeEnd(view)
+
+            rule.runOnIdle {
+                assertThat(coordinates.boundsInRoot())
+                    .isEqualTo(Rect(10f, 11f, width - 12f, height - 20f))
+            }
+        }
+    }
+
+    @Test
+    fun paddingValues() {
+        lateinit var coordinates: LayoutCoordinates
+
+        setContent {
+            val padding = WindowInsets.systemBars.asPaddingValues()
+            Box(Modifier.fillMaxSize().padding(padding)) {
+                Box(Modifier.fillMaxSize().onGloballyPositioned { coordinates = it })
+            }
+        }
+
+        // wait for layout
+        rule.waitForIdle()
+
+        val insets = sendInsets(WindowInsetsCompat.Type.systemBars())
+        insets.assertIsConsumed(WindowInsetsCompat.Type.systemBars())
+
+        rule.runOnIdle {
+            val view = insetsView.findComposeView()
+            val width = view.width
+            val height = view.height
+            val expectedRect = Rect(10f, 11f, width - 12f, height - 13f)
+            assertThat(coordinates.boundsInRoot()).isEqualTo(expectedRect)
+        }
+    }
+
+    // Each level of the padding should consume some parts of the insets
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun consumeAtEachDepth() {
+        lateinit var statusBar: LayoutCoordinates
+        lateinit var navigationBar: LayoutCoordinates
+        lateinit var ime: LayoutCoordinates
+        setContent {
+            Box(
+                Modifier
+                    .fillMaxSize()
+                    .statusBarsPadding()
+                    .onGloballyPositioned { statusBar = it }
+            ) {
+                Box(Modifier.navigationBarsPadding().onGloballyPositioned { navigationBar = it }) {
+                    Box(Modifier.imePadding().fillMaxSize().onGloballyPositioned { ime = it })
+                }
+            }
+        }
+        // wait for layout
+        rule.waitForIdle()
+
+        val insets = WindowInsetsCompat.Builder()
+            .setInsets(WindowInsetsCompat.Type.statusBars(), AndroidXInsets.of(0, 10, 0, 0))
+            .setInsets(WindowInsetsCompat.Type.navigationBars(), AndroidXInsets.of(0, 0, 0, 11))
+            .setInsets(WindowInsetsCompat.Type.ime(), AndroidXInsets.of(0, 10, 0, 20))
+            .build()
+
+        dispatchApplyWindowInsets(insets)
+
+        rule.runOnIdle {
+            val height = insetsView.findComposeView().height
+            assertThat(statusBar.size.height).isEqualTo(height - 10)
+            assertThat(navigationBar.size.height).isEqualTo(height - 21)
+            assertThat(ime.size.height).isEqualTo(height - 30)
+        }
+    }
+
+    // The consumedPaddingInsets() should remove the insets values so that they aren't consumed
+    // further down the hierarchy.
+    @OptIn(ExperimentalLayoutApi::class)
+    @Test
+    fun consumedInsetsPadding() {
+        lateinit var outer: LayoutCoordinates
+        lateinit var middle: LayoutCoordinates
+        lateinit var inner: LayoutCoordinates
+        setContent {
+            with(LocalDensity.current) {
+                Box(
+                    Modifier
+                        .fillMaxSize()
+                        .consumedWindowInsets(PaddingValues(top = 1.toDp()))
+                        .windowInsetsPadding(WindowInsets(top = 10))
+                        .onGloballyPositioned { outer = it }
+                ) {
+                    Box(Modifier
+                        .consumedWindowInsets(PaddingValues(top = 1.toDp()))
+                        .windowInsetsPadding(WindowInsets(top = 20))
+                        .onGloballyPositioned { middle = it }
+                    ) {
+                        Box(
+                            Modifier
+                                .consumedWindowInsets(PaddingValues(top = 1.toDp()))
+                                .windowInsetsPadding(WindowInsets(top = 30))
+                                .fillMaxSize()
+                                .onGloballyPositioned { inner = it }
+                        )
+                    }
+                }
+            }
+        }
+        // wait for layout
+        rule.waitForIdle()
+
+        val insets = WindowInsetsCompat.Builder()
+            .setInsets(WindowInsetsCompat.Type.statusBars(), AndroidXInsets.of(0, 35, 0, 0))
+            .build()
+
+        dispatchApplyWindowInsets(insets)
+
+        rule.runOnIdle {
+            val height = insetsView.findComposeView().height
+            assertThat(outer.size.height).isEqualTo(height - 9)
+            assertThat(middle.size.height).isEqualTo(height - 18)
+            assertThat(inner.size.height).isEqualTo(height - 27)
+        }
+    }
+
+    // The consumedInsets() should remove only values that haven't been consumed.
+    @OptIn(ExperimentalLayoutApi::class)
+    @Test
+    fun consumedInsetsLimitedConsumption() {
+        lateinit var outer: LayoutCoordinates
+        lateinit var middle: LayoutCoordinates
+        lateinit var inner: LayoutCoordinates
+        setContent {
+            Box(
+                Modifier
+                    .fillMaxSize()
+                    .consumedWindowInsets(WindowInsets(top = 1))
+                    .windowInsetsPadding(WindowInsets(top = 10))
+                    .onGloballyPositioned { outer = it }
+            ) {
+                Box(Modifier
+                    .consumedWindowInsets(WindowInsets(top = 10))
+                    .windowInsetsPadding(WindowInsets(top = 20))
+                    .onGloballyPositioned { middle = it }
+                ) {
+                    Box(
+                        Modifier
+                            .consumedWindowInsets(WindowInsets(top = 20))
+                            .windowInsetsPadding(WindowInsets(top = 30))
+                            .fillMaxSize()
+                            .onGloballyPositioned { inner = it }
+                    )
+                }
+            }
+        }
+        // wait for layout
+        rule.waitForIdle()
+
+        val insets = WindowInsetsCompat.Builder()
+            .setInsets(WindowInsetsCompat.Type.statusBars(), AndroidXInsets.of(0, 35, 0, 0))
+            .build()
+
+        dispatchApplyWindowInsets(insets)
+
+        rule.runOnIdle {
+            val height = insetsView.findComposeView().height
+            assertThat(outer.size.height).isEqualTo(height - 9)
+            assertThat(middle.size.height).isEqualTo(height - 19)
+            assertThat(inner.size.height).isEqualTo(height - 29)
+        }
+    }
+
+    // When the insets change, the layout should be redrawn.
+    @OptIn(ExperimentalLayoutApi::class)
+    @Test
+    fun newInsetsCausesLayout() {
+        lateinit var coordinates: LayoutCoordinates
+        var useMiddleInsets by mutableStateOf(true)
+
+        setContent {
+            Box(Modifier.fillMaxSize()) {
+                val modifier = if (useMiddleInsets) {
+                    Modifier.consumedWindowInsets(WindowInsets(top = 1))
+                } else {
+                    Modifier.consumedWindowInsets(WindowInsets(top = 2))
+                }
+                with(LocalDensity.current) {
+                    Box(modifier.size(50.toDp())) {
+                        Box(
+                            Modifier
+                                .windowInsetsPadding(WindowInsets(top = 10))
+                                .fillMaxSize()
+                                .onGloballyPositioned { coordinates = it }
+                        )
+                    }
+                }
+            }
+        }
+
+        // wait for layout
+        rule.waitForIdle()
+
+        sendInsets(WindowInsetsCompat.Type.statusBars(), AndroidXInsets.of(0, 20, 0, 0))
+
+        rule.runOnIdle {
+            assertThat(coordinates.size.height).isEqualTo(41)
+            useMiddleInsets = false
+        }
+
+        rule.runOnIdle {
+            assertThat(coordinates.size.height).isEqualTo(42)
+        }
+    }
+
+    private fun sendInsets(
+        type: Int,
+        sentInsets: AndroidXInsets = AndroidXInsets.of(10, 11, 12, 13)
+    ): WindowInsetsCompat {
+        val insets = WindowInsetsCompat.Builder()
+            .setInsets(type, sentInsets)
+            .build()
+        return dispatchApplyWindowInsets(insets)
+    }
+
+    private fun dispatchApplyWindowInsets(insets: WindowInsetsCompat): WindowInsetsCompat {
+        return rule.runOnIdle {
+            val windowInsets = insets.toWindowInsets()!!
+            val view = insetsView
+            insetsView.myInsets = windowInsets
+            val returnedInsets = view.findComposeView().dispatchApplyWindowInsets(windowInsets)
+            WindowInsetsCompat.toWindowInsetsCompat(returnedInsets, view)
+        }
+    }
+
+    private fun setInsetContent(
+        insetsModifier: @Composable () -> Modifier
+    ): LayoutCoordinates {
+        lateinit var coordinates: LayoutCoordinates
+
+        setContent {
+            Box(Modifier.fillMaxSize().background(Color.Blue).then(insetsModifier())) {
+                Box(Modifier.fillMaxSize().onGloballyPositioned {
+                    coordinates = it
+                })
+            }
+        }
+
+        // wait for layout
+        rule.waitForIdle()
+        return coordinates
+    }
+
+    private fun setContent(content: @Composable () -> Unit) {
+        rule.setContent {
+            AndroidView(factory = { context ->
+                val view = InsetsView(context)
+                insetsView = view
+                val composeView = ComposeView(rule.activity)
+                view.addView(
+                    composeView,
+                    ViewGroup.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT
+                    )
+                )
+                composeView.setContent(content)
+                view
+            }, modifier = Modifier.fillMaxSize())
+        }
+    }
+
+    private fun WindowInsetsCompat.assertIsConsumed(type: Int) {
+        val insets = getInsets(type)
+        assertThat(insets).isEqualTo(AndroidXInsets.of(0, 0, 0, 0))
+    }
+
+    private fun WindowInsetsCompat.assertIsNotConsumed(type: Int) {
+        val insets = getInsets(type)
+        assertThat(insets).isNotEqualTo(AndroidXInsets.of(0, 0, 0, 0))
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.R)
+private class Api30Methods(
+    val rule: AndroidComposeTestRule<ActivityScenarioRule<ComponentActivity>, ComponentActivity>
+) {
+    fun sendImeStart(view: View): WindowInsetsAnimation {
+        return rule.runOnIdle {
+            val animation =
+                WindowInsetsAnimation(AndroidWindowInsets.Type.ime(), LinearInterpolator(), 100L)
+            view.dispatchWindowInsetsAnimationPrepare(animation)
+
+            val imeInsets = FrameworkInsets.of(0, 0, 0, 20)
+            val bounds = WindowInsetsAnimation.Bounds(
+                FrameworkInsets.NONE,
+                imeInsets
+            )
+            view.dispatchWindowInsetsAnimationStart(animation, bounds)
+            animation
+        }
+    }
+
+    fun WindowInsetsAnimation.sendImeProgress(view: View, progress: Float) {
+        return rule.runOnIdle {
+            val bottom = (20 * progress).roundToInt()
+            val imeInsets = FrameworkInsets.of(0, 0, 0, bottom)
+            val systemBarsInsets = FrameworkInsets.of(10, 11, 12, 13)
+            val animatedInsets = AndroidWindowInsets.Builder()
+                .setInsets(AndroidWindowInsets.Type.systemBars(), systemBarsInsets)
+                .setInsets(AndroidWindowInsets.Type.ime(), imeInsets)
+                .build()
+
+            val progressInsets =
+                view.dispatchWindowInsetsAnimationProgress(animatedInsets, listOf(this))
+            assertThat(progressInsets.isConsumed).isTrue()
+        }
+    }
+
+    fun WindowInsetsAnimation.sendImeEnd(view: View) {
+        rule.runOnIdle {
+            view.dispatchWindowInsetsAnimationEnd(this)
+        }
+    }
+}
+
+/**
+ * A View below the compose View that overrides the insets sent by the system. The
+ * compat onApplyWindowInsets listener calls requestApplyInsets(), which results in
+ * the insets being sent again. If we don't override the insets then the system insets
+ * (which are likely 0) will override the insets that we set in the test.
+ */
+internal class InsetsView(context: Context) : FrameLayout(context) {
+    var myInsets: AndroidWindowInsets? = null
+
+    override fun dispatchApplyWindowInsets(insets: AndroidWindowInsets): AndroidWindowInsets {
+        return super.dispatchApplyWindowInsets(myInsets ?: insets)
+    }
+
+    fun findComposeView(): View = findComposeView(this)!!
+
+    private companion object {
+        fun findComposeView(view: View): View? {
+            if (view is ViewRootForTest) {
+                return view
+            } else if (view is ViewGroup) {
+                view.forEach { child ->
+                    val composeView = findComposeView(child)
+                    if (composeView != null) {
+                        return composeView
+                    }
+                }
+            }
+            return null
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsSizeTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsSizeTest.kt
new file mode 100644
index 0000000..e987593
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsSizeTest.kt
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2022 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.foundation.layout
+
+import android.graphics.Rect as AndroidRect
+import android.view.WindowInsets as AndroidWindowInsets
+import android.os.Build
+import android.view.View
+import android.view.ViewGroup
+import androidx.activity.ComponentActivity
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.graphics.Insets as AndroidXInsets
+import androidx.core.view.DisplayCutoutCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class WindowInsetsSizeTest {
+    @get:Rule
+    val rule = createAndroidComposeRule<ComponentActivity>()
+
+    private lateinit var insetsView: InsetsView
+
+    @Before
+    fun setup() {
+        WindowInsetsHolder.setUseTestInsets(true)
+    }
+
+    @After
+    fun teardown() {
+        WindowInsetsHolder.setUseTestInsets(false)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun insetsStartWidthIme() {
+        testInsetsSize(
+            WindowInsetsCompat.Type.ime(),
+            { Modifier.windowInsetsStartWidth(WindowInsets.ime).fillMaxHeight() },
+            AndroidXInsets.of(10, 0, 0, 0),
+            LayoutDirection.Ltr
+        ) { size -> IntSize(10, size.height) }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun insetsStartWidthImeRtl() {
+        testInsetsSize(
+            WindowInsetsCompat.Type.ime(),
+            { Modifier.windowInsetsStartWidth(WindowInsets.ime).fillMaxHeight() },
+            AndroidXInsets.of(0, 0, 10, 0),
+            LayoutDirection.Rtl
+        ) { size -> IntSize(10, size.height) }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun insetsEndWidthIme() {
+        testInsetsSize(
+            WindowInsetsCompat.Type.ime(),
+            { Modifier.windowInsetsEndWidth(WindowInsets.ime).fillMaxHeight() },
+            AndroidXInsets.of(0, 0, 10, 0),
+            LayoutDirection.Ltr
+        ) { size -> IntSize(10, size.height) }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun insetsTopHeightIme() {
+        testInsetsSize(
+            WindowInsetsCompat.Type.ime(),
+            { Modifier.windowInsetsTopHeight(WindowInsets.ime).fillMaxWidth() },
+            AndroidXInsets.of(0, 10, 0, 0),
+            LayoutDirection.Ltr
+        ) { size -> IntSize(size.width, 10) }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun insetsBottomHeightIme() {
+        testInsetsSize(
+            WindowInsetsCompat.Type.ime(),
+            { Modifier.windowInsetsBottomHeight(WindowInsets.ime).fillMaxWidth() },
+            AndroidXInsets.of(0, 0, 0, 10),
+            LayoutDirection.Ltr
+        ) { size -> IntSize(size.width, 10) }
+    }
+
+    @Test
+    fun insetsStartWidthNavigationBars() {
+        testInsetsSize(
+            WindowInsetsCompat.Type.navigationBars(),
+            { Modifier.windowInsetsStartWidth(WindowInsets.navigationBars).fillMaxHeight() },
+            AndroidXInsets.of(10, 0, 0, 0),
+            LayoutDirection.Ltr
+        ) { size -> IntSize(10, size.height) }
+    }
+
+    @Test
+    fun insetsStartWidthNavigationBarsRtl() {
+        testInsetsSize(
+            WindowInsetsCompat.Type.navigationBars(),
+            { Modifier.windowInsetsStartWidth(WindowInsets.navigationBars).fillMaxHeight() },
+            AndroidXInsets.of(0, 0, 10, 0),
+            LayoutDirection.Rtl
+        ) { size -> IntSize(10, size.height) }
+    }
+
+    @Test
+    fun insetsEndWidthNavigationBars() {
+        testInsetsSize(
+            WindowInsetsCompat.Type.navigationBars(),
+            { Modifier.windowInsetsEndWidth(WindowInsets.navigationBars).fillMaxHeight() },
+            AndroidXInsets.of(0, 0, 10, 0),
+            LayoutDirection.Ltr
+        ) { size -> IntSize(10, size.height) }
+    }
+
+    @Test
+    fun insetsTopHeightStatusBars() {
+        testInsetsSize(
+            WindowInsetsCompat.Type.statusBars(),
+            { Modifier.windowInsetsTopHeight(WindowInsets.statusBars).fillMaxWidth() },
+            AndroidXInsets.of(0, 10, 0, 0),
+            LayoutDirection.Ltr
+        ) { size -> IntSize(size.width, 10) }
+    }
+
+    @Test
+    fun insetsTopHeightMixed() {
+        val coordinates = setInsetContent(
+            {
+                val insets = WindowInsets
+                Modifier.windowInsetsTopHeight(insets.navigationBars.union(insets.systemBars))
+                    .fillMaxWidth()
+            },
+            LayoutDirection.Ltr
+        )
+        val insets = WindowInsetsCompat.Builder()
+            .setInsets(WindowInsetsCompat.Type.navigationBars(), AndroidXInsets.of(0, 3, 0, 0))
+            .setInsets(WindowInsetsCompat.Type.systemBars(), AndroidXInsets.of(0, 10, 0, 0))
+            .build()
+
+        val view = findComposeView()
+        rule.runOnIdle {
+            insetsView.myInsets = insets.toWindowInsets()
+            view.dispatchApplyWindowInsets(insets.toWindowInsets())
+        }
+
+        rule.runOnIdle {
+            assertThat(coordinates.size).isEqualTo(IntSize(view.width, 10))
+        }
+    }
+
+    @Test
+    fun topHeightModifiersAreEqual() {
+        rule.setContent {
+            val modifier1 = Modifier.windowInsetsTopHeight(WindowInsets.statusBars)
+            val modifier2 = Modifier.windowInsetsTopHeight(WindowInsets.statusBars)
+            assertThat(modifier1).isEqualTo(modifier2)
+        }
+    }
+
+    @Test
+    fun bottomHeightModifiersAreEqual() {
+        rule.setContent {
+            val modifier1 = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)
+            val modifier2 = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)
+            assertThat(modifier1).isEqualTo(modifier2)
+        }
+    }
+
+    @Test
+    fun startWidthModifiersAreEqual() {
+        rule.setContent {
+            val modifier1 = Modifier.windowInsetsStartWidth(WindowInsets.navigationBars)
+            val modifier2 = Modifier.windowInsetsStartWidth(WindowInsets.navigationBars)
+            assertThat(modifier1).isEqualTo(modifier2)
+        }
+    }
+
+    @Test
+    fun endWidthModifiersAreEqual() {
+        rule.setContent {
+            val modifier1 = Modifier.windowInsetsEndWidth(WindowInsets.navigationBars)
+            val modifier2 = Modifier.windowInsetsEndWidth(WindowInsets.navigationBars)
+            assertThat(modifier1).isEqualTo(modifier2)
+        }
+    }
+
+    private fun testInsetsSize(
+        type: Int,
+        modifier: @Composable () -> Modifier,
+        sentInsets: AndroidXInsets,
+        layoutDirection: LayoutDirection,
+        expected: (IntSize) -> IntSize
+    ) {
+        val coordinates = setInsetContent(modifier, layoutDirection)
+
+        val insets = sendInsets(type, sentInsets)
+        assertThat(insets.isConsumed)
+
+        rule.runOnIdle {
+            val view = findComposeView()
+            val width = view.width
+            val height = view.height
+            val expectedSize = expected(IntSize(width, height))
+            assertThat(coordinates.size).isEqualTo(expectedSize)
+        }
+    }
+
+    private fun sendInsets(
+        type: Int,
+        sentInsets: AndroidXInsets = AndroidXInsets.of(10, 11, 12, 13)
+    ): AndroidWindowInsets {
+        val builder = WindowInsetsCompat.Builder()
+            .setInsets(type, sentInsets)
+        if (type == WindowInsetsCompat.Type.displayCutout()) {
+            val view = findComposeView()
+            val width = view.width
+            val height = view.height
+            val safeRect = AndroidRect(0, 0, width, height)
+            val cutoutRect =
+                AndroidRect(width / 2 - 5, height / 2 - 5, width / 2 + 5, height / 2 + 5)
+            when {
+                sentInsets.left > 0 -> {
+                    safeRect.left = sentInsets.left
+                    cutoutRect.left = 0
+                    cutoutRect.right = safeRect.left
+                }
+                sentInsets.top > 0 -> {
+                    safeRect.top = sentInsets.top
+                    cutoutRect.top = 0
+                    cutoutRect.bottom = safeRect.top
+                }
+                sentInsets.right > 0 -> {
+                    safeRect.right = width - sentInsets.right
+                    cutoutRect.right = width
+                    cutoutRect.left = width - safeRect.right
+                }
+                sentInsets.bottom > 0 -> {
+                    safeRect.bottom = sentInsets.bottom
+                    cutoutRect.bottom = height
+                    cutoutRect.top = height - safeRect.bottom
+                }
+            }
+            builder.setDisplayCutout(DisplayCutoutCompat(safeRect, listOf(cutoutRect)))
+        }
+        val insets = WindowInsetsCompat.Builder()
+            .setInsets(type, sentInsets)
+            .build()
+        insetsView.myInsets = insets.toWindowInsets()
+        return rule.runOnIdle {
+            AndroidWindowInsets(
+                findComposeView().dispatchApplyWindowInsets(insets.toWindowInsets())
+            )
+        }
+    }
+
+    private fun setInsetContent(
+        sizeModifier: @Composable () -> Modifier,
+        layoutDirection: LayoutDirection
+    ): LayoutCoordinates {
+        lateinit var coordinates: LayoutCoordinates
+
+        rule.setContent {
+            AndroidView(factory = { context ->
+                val view = InsetsView(context)
+                insetsView = view
+                val composeView = ComposeView(rule.activity)
+                view.addView(
+                    composeView,
+                    ViewGroup.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT
+                    )
+                )
+                composeView.setContent {
+                    CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+                        Box(Modifier.wrapContentSize().onGloballyPositioned { coordinates = it }) {
+                            Box(sizeModifier())
+                        }
+                    }
+                }
+                view
+            }, modifier = Modifier.fillMaxSize())
+        }
+
+        // wait for layout
+        rule.waitForIdle()
+        return coordinates
+    }
+
+    private fun findComposeView(): View = insetsView.findComposeView()
+}
diff --git a/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsets.android.kt b/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsets.android.kt
new file mode 100644
index 0000000..bfc442d
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsets.android.kt
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2022 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.foundation.layout
+
+import androidx.core.graphics.Insets as AndroidXInsets
+import android.os.Build
+import android.view.View
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.ui.platform.LocalView
+import androidx.core.view.OnApplyWindowInsetsListener
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsAnimationCompat
+import androidx.core.view.WindowInsetsCompat
+import java.util.WeakHashMap
+import org.jetbrains.annotations.TestOnly
+
+internal fun AndroidXInsets.toInsetsValues(): InsetsValues =
+    InsetsValues(left, top, right, bottom)
+
+internal fun ValueInsets(insets: AndroidXInsets, name: String): ValueInsets =
+    ValueInsets(insets.toInsetsValues(), name)
+
+/**
+ * For the [WindowInsetsCompat.Type.captionBar].
+ */
+val WindowInsets.Companion.captionBar: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().captionBar
+
+/**
+ * For the [WindowInsetsCompat.Type.displayCutout]. This insets represents the area that the
+ * display cutout (e.g. for camera) is and important content should be excluded from.
+ */
+val WindowInsets.Companion.displayCutout: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().displayCutout
+
+/**
+ * For the [WindowInsetsCompat.Type.ime]. On [Build.VERSION_CODES.R] and above, the
+ * soft keyboard can be detected and [ime] will animate when it shows.
+ *
+ * Developers should set `android:windowSoftInputMode="adjustResize"` in their
+ * `AndroidManifest.xml` file and call `WindowCompat.setDecorFitsSystemWindows(window, false)`
+ * in their [android.app.Activity.onCreate].
+ */
+val WindowInsets.Companion.ime: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().ime
+
+/**
+ * For the [WindowInsetsCompat.Type.mandatorySystemGestures]. These insets represents the
+ * space where system gestures have priority over application gestures.
+ */
+val WindowInsets.Companion.mandatorySystemGestures: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().mandatorySystemGestures
+
+/**
+ * For the [WindowInsetsCompat.Type.navigationBars]. These insets represent where
+ * system UI places navigation bars. Interactive UI should avoid the navigation bars
+ * area.
+ */
+val WindowInsets.Companion.navigationBars: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().navigationBars
+
+/**
+ * For the [WindowInsetsCompat.Type.statusBars].
+ */
+val WindowInsets.Companion.statusBars: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().statusBars
+
+/**
+ * For the [WindowInsetsCompat.Type.systemBars].
+ */
+val WindowInsets.Companion.systemBars: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().systemBars
+
+/**
+ * For the [WindowInsetsCompat.Type.systemGestures].
+ */
+val WindowInsets.Companion.systemGestures: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().systemGestures
+
+/**
+ * For the [WindowInsetsCompat.Type.tappableElement].
+ */
+val WindowInsets.Companion.tappableElement: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().tappableElement
+
+/**
+ * The insets for the curved areas in a waterfall display.
+ */
+val WindowInsets.Companion.waterfall: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().waterfall
+
+/**
+ * The insets that include areas where content may be covered by other drawn content.
+ * This includes all [system bars][systemBars], [display cutout][displayCutout], and
+ * [soft keyboard][ime].
+ */
+val WindowInsets.Companion.safeDrawing: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().safeDrawing
+
+/**
+ * The insets that include areas where gestures may be confused with other input,
+ * including [system gestures][systemGestures],
+ * [mandatory system gestures][mandatorySystemGestures],
+ * [rounded display areas][waterfall], and [tappable areas][tappableElement].
+ */
+val WindowInsets.Companion.safeGestures: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().safeGestures
+
+/**
+ * The insets that include all areas that may be drawn over or have gesture confusion,
+ * including everything in [safeDrawing] and [safeGestures].
+ */
+val WindowInsets.Companion.safeContent: WindowInsets
+    @Composable
+    @NonRestartableComposable
+    get() = WindowInsetsHolder.current().safeContent
+
+/**
+ * The insets for various values in the current window.
+ */
+internal class WindowInsetsHolder private constructor(insets: WindowInsetsCompat?) {
+    val captionBar =
+        valueInsets(insets, WindowInsetsCompat.Type.captionBar(), "captionBar")
+    val displayCutout =
+        valueInsets(insets, WindowInsetsCompat.Type.displayCutout(), "displayCutout")
+    val ime = valueInsets(insets, WindowInsetsCompat.Type.ime(), "ime")
+    val mandatorySystemGestures = valueInsets(
+        insets,
+        WindowInsetsCompat.Type.mandatorySystemGestures(),
+        "mandatorySystemGestures"
+    )
+    val navigationBars =
+        valueInsets(insets, WindowInsetsCompat.Type.navigationBars(), "navigationBars")
+    val statusBars =
+        valueInsets(insets, WindowInsetsCompat.Type.statusBars(), "statusBars")
+    val systemBars =
+        valueInsets(insets, WindowInsetsCompat.Type.systemBars(), "systemBars")
+    val systemGestures =
+        valueInsets(insets, WindowInsetsCompat.Type.systemGestures(), "systemGestures")
+    val tappableElement =
+        valueInsets(insets, WindowInsetsCompat.Type.tappableElement(), "tappableElement")
+    val waterfall =
+        ValueInsets(insets?.displayCutout?.waterfallInsets ?: AndroidXInsets.NONE, "waterfall")
+    val safeDrawing =
+        systemBars.union(ime).union(displayCutout)
+    val safeGestures: WindowInsets =
+        tappableElement.union(mandatorySystemGestures).union(systemGestures).union(waterfall)
+    val safeContent: WindowInsets = safeDrawing.union(safeGestures)
+
+    /**
+     * The number of accesses to [WindowInsetsHolder]. When this reaches
+     * zero, the listeners are removed. When it increases to 1, the listeners are added.
+     */
+    private var consumers = 0
+
+    private val insetsListener = InsetsListener(this)
+
+    /**
+     * A usage of [WindowInsetsHolder.current] was added. We must track so that when the
+     * first one is added, listeners are set and when the last is removed, the listeners
+     * are removed.
+     */
+    fun incrementConsumers(view: View) {
+        if (consumers == 0) {
+            // add listeners
+            ViewCompat.setOnApplyWindowInsetsListener(view, insetsListener)
+
+            // We don't need animation callbacks on earlier versions, so don't bother adding
+            // the listener. ViewCompat calls the animation callbacks superfluously.
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+                ViewCompat.setWindowInsetsAnimationCallback(view, insetsListener)
+            }
+        }
+        consumers++
+    }
+
+    /**
+     * A usage of [WindowInsetsHolder.current] was removed. We must track so that when the
+     * first one is added, listeners are set and when the last is removed, the listeners
+     * are removed.
+     */
+    fun decrementConsumers(view: View) {
+        consumers--
+        if (consumers == 0) {
+            // remove listeners
+            ViewCompat.setOnApplyWindowInsetsListener(view, null)
+            ViewCompat.setWindowInsetsAnimationCallback(view, null)
+        }
+    }
+
+    /**
+     * Updates the WindowInsets values and notifies changes.
+     */
+    fun update(windowInsets: WindowInsetsCompat) {
+        Snapshot.withMutableSnapshot {
+            val insets = if (testInsets) {
+                // WindowInsetsCompat erases insets that aren't part of the device.
+                // For example, if there is no navigation bar because of hardware keys,
+                // the bottom navigation bar will be removed. By using the constructor
+                // that doesn't accept a View, it doesn't remove the insets that aren't
+                // possible. This is important for testing on arbitrary hardware.
+                WindowInsetsCompat.toWindowInsetsCompat(windowInsets.toWindowInsets()!!)
+            } else {
+                windowInsets
+            }
+            captionBar.value =
+                insets.getInsets(WindowInsetsCompat.Type.captionBar()).toInsetsValues()
+            ime.value =
+                insets.getInsets(WindowInsetsCompat.Type.ime()).toInsetsValues()
+            displayCutout.value =
+                insets.getInsets(WindowInsetsCompat.Type.displayCutout()).toInsetsValues()
+            navigationBars.value =
+                insets.getInsets(WindowInsetsCompat.Type.navigationBars()).toInsetsValues()
+            statusBars.value =
+                insets.getInsets(WindowInsetsCompat.Type.statusBars()).toInsetsValues()
+            systemBars.value =
+                insets.getInsets(WindowInsetsCompat.Type.systemBars()).toInsetsValues()
+            systemGestures.value =
+                insets.getInsets(WindowInsetsCompat.Type.systemGestures()).toInsetsValues()
+            tappableElement.value =
+                insets.getInsets(WindowInsetsCompat.Type.tappableElement()).toInsetsValues()
+            mandatorySystemGestures.value =
+                insets.getInsets(WindowInsetsCompat.Type.mandatorySystemGestures()).toInsetsValues()
+
+            val cutout = insets.displayCutout
+            if (cutout != null) {
+                val waterfallInsets = cutout.waterfallInsets
+                waterfall.value = waterfallInsets.toInsetsValues()
+            }
+        }
+    }
+
+    companion object {
+        /**
+         * A mapping of AndroidComposeView to ComposeWindowInsets. Normally a tag is a great
+         * way to do this mapping, but off-UI thread and multithreaded composition don't
+         * allow using the tag.
+         */
+        private val viewMap = WeakHashMap<View, WindowInsetsHolder>()
+
+        private var testInsets = false
+
+        /**
+         * Testing Window Insets is difficult, so we have this to help eliminate device-specifics
+         * from the WindowInsets. This is indirect because `@TestOnly` cannot be applied to a
+         * property with a backing field.
+         */
+        @TestOnly
+        fun setUseTestInsets(testInsets: Boolean) {
+            this.testInsets = testInsets
+        }
+
+        @Composable
+        fun current(): WindowInsetsHolder {
+            val view = LocalView.current
+            val insets = getOrCreateFor(view)
+
+            DisposableEffect(insets) {
+                insets.incrementConsumers(view)
+                onDispose {
+                    insets.decrementConsumers(view)
+                }
+            }
+            return insets
+        }
+
+        /**
+         * Returns the [WindowInsetsHolder] associated with [view] or creates one and associates
+         * it.
+         */
+        private fun getOrCreateFor(view: View): WindowInsetsHolder {
+            return synchronized(viewMap) {
+                viewMap.getOrPut(view) {
+                    val insets = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                        RootWindowInsetsApi23.rootWindowInsets(view)
+                    } else {
+                        null
+                    }
+                    WindowInsetsHolder(insets)
+                }
+            }
+        }
+
+        /**
+         * Creates a [ValueInsets] using the value from [windowInsets] if it isn't `null`
+         */
+        private fun valueInsets(
+            windowInsets: WindowInsetsCompat?,
+            type: Int,
+            name: String
+        ): ValueInsets {
+            val initial = windowInsets?.getInsets(type) ?: AndroidXInsets.NONE
+            return ValueInsets(initial, name)
+        }
+    }
+}
+
+/**
+ * Used to get the [View.getRootWindowInsets] only on M and above
+ */
+@RequiresApi(Build.VERSION_CODES.M)
+private object RootWindowInsetsApi23 {
+    @DoNotInline
+    fun rootWindowInsets(view: View): WindowInsetsCompat? {
+        return view.rootWindowInsets?.let {
+            WindowInsetsCompat.toWindowInsetsCompat(it, view)
+        }
+    }
+}
+
+private class InsetsListener(
+    val composeInsets: WindowInsetsHolder,
+) : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP), OnApplyWindowInsetsListener {
+
+    override fun onProgress(
+        insets: WindowInsetsCompat,
+        runningAnimations: MutableList<WindowInsetsAnimationCompat>
+    ): WindowInsetsCompat {
+        composeInsets.update(insets)
+        return WindowInsetsCompat.CONSUMED
+    }
+
+    override fun onApplyWindowInsets(view: View, insets: WindowInsetsCompat): WindowInsetsCompat {
+        composeInsets.update(insets)
+        return WindowInsetsCompat.CONSUMED
+    }
+}
diff --git a/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsetsPadding.android.kt b/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsetsPadding.android.kt
new file mode 100644
index 0000000..244bf86
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsetsPadding.android.kt
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2022 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.foundation.layout
+
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+
+/**
+ * Adds padding to accommodate the [safe drawing][WindowInsets.Companion.safeDrawing] insets.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.safeDrawing] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [statusBarsPadding], the area that the parent
+ * pads for the status bars will not be padded again by this [safeDrawingPadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.safeDrawingPaddingSample
+ */
+fun Modifier.safeDrawingPadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "safeDrawingPadding" }) { safeDrawing }
+
+/**
+ * Adds padding to accommodate the [safe gestures][WindowInsets.Companion.safeGestures] insets.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.safeGestures] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [navigationBarsPadding],
+ * the area that the parent layout pads for the status bars will not be padded again by this
+ * [safeGesturesPadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.safeGesturesPaddingSample
+ */
+fun Modifier.safeGesturesPadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "safeGesturesPadding" }) { safeGestures }
+
+/**
+ * Adds padding to accommodate the [safe content][WindowInsets.Companion.safeContent] insets.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.safeContent] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [navigationBarsPadding],
+ * the area that the parent layout pads for the status bars will not be padded again by this
+ * [safeContentPadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.safeContentPaddingSample
+ */
+fun Modifier.safeContentPadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "safeContentPadding" }) { safeContent }
+
+/**
+ * Adds padding to accommodate the [system bars][WindowInsets.Companion.systemBars] insets.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.systemBars] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [statusBarsPadding], the
+ * area that the parent layout pads for the status bars will not be padded again by this
+ * [systemBarsPadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.systemBarsPaddingSample
+ */
+fun Modifier.systemBarsPadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "systemBarsPadding" }) { systemBars }
+
+/**
+ * Adds padding to accommodate the [display cutout][WindowInsets.Companion.displayCutout].
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.displayCutout] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [statusBarsPadding], the
+ * area that the parent layout pads for the status bars will not be padded again by this
+ * [displayCutoutPadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.displayCutoutPaddingSample
+ */
+fun Modifier.displayCutoutPadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "displayCutoutPadding" }) { displayCutout }
+
+/**
+ * Adds padding to accommodate the [status bars][WindowInsets.Companion.statusBars] insets.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.statusBars] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [displayCutoutPadding], the
+ * area that the parent layout pads for the status bars will not be padded again by this
+ * [statusBarsPadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.statusBarsAndNavigationBarsPaddingSample
+ */
+fun Modifier.statusBarsPadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "statusBarsPadding" }) { statusBars }
+
+/**
+ * Adds padding to accommodate the [ime][WindowInsets.Companion.ime] insets.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.ime] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [navigationBarsPadding],
+ * the area that the parent layout pads for the status bars will not be padded again by this
+ * [imePadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.imePaddingSample
+ */
+fun Modifier.imePadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "imePadding" }) { ime }
+
+/**
+ * Adds padding to accommodate the [navigation bars][WindowInsets.Companion.navigationBars] insets.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.navigationBars] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [systemBarsPadding], the
+ * area that the parent layout pads for the status bars will not be padded again by this
+ * [navigationBarsPadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.statusBarsAndNavigationBarsPaddingSample
+ */
+fun Modifier.navigationBarsPadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "navigationBarsPadding" }) { navigationBars }
+
+/**
+ * Adds padding to accommodate the [caption bar][WindowInsets.Companion.captionBar] insets.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.captionBar] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [displayCutoutPadding], the
+ * area that the parent layout pads for the status bars will not be padded again by this
+ * [captionBarPadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.captionBarPaddingSample
+ */
+fun Modifier.captionBarPadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "captionBarPadding" }) { captionBar }
+
+/**
+ * Adds padding to accommodate the [waterfall][WindowInsets.Companion.waterfall] insets.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.waterfall] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [systemGesturesPadding],
+ * the area that the parent layout pads for the status bars will not be padded again by this
+ * [waterfallPadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.waterfallPaddingSample
+ */
+fun Modifier.waterfallPadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "waterfallPadding" }) { waterfall }
+
+/**
+ * Adds padding to accommodate the [system gestures][WindowInsets.Companion.systemGestures] insets.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.systemGestures] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [waterfallPadding], the
+ * area that the parent layout pads for the status bars will not be padded again by this
+ * [systemGesturesPadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.systemGesturesPaddingSample
+ */
+fun Modifier.systemGesturesPadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "systemGesturesPadding" }) { systemGestures }
+
+/**
+ * Adds padding to accommodate the
+ * [mandatory system gestures][WindowInsets.Companion.mandatorySystemGestures] insets.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent layout
+ * will be excluded from the padding. [WindowInsets.Companion.mandatorySystemGestures] will be
+ * [consumed][consumedWindowInsets] for child layouts as well.
+ *
+ * For example, if a parent layout uses [navigationBarsPadding],
+ * the area that the parent layout pads for the status bars will not be padded again by this
+ * [mandatorySystemGesturesPadding] modifier.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.mandatorySystemGesturesPaddingSample
+ */
+fun Modifier.mandatorySystemGesturesPadding() =
+    windowInsetsPadding(debugInspectorInfo { name = "mandatorySystemGesturesPadding" }) {
+        mandatorySystemGestures
+    }
+
+@Suppress("NOTHING_TO_INLINE", "ModifierInspectorInfo")
+@Stable
+private inline fun Modifier.windowInsetsPadding(
+    noinline inspectorInfo: InspectorInfo.() -> Unit,
+    crossinline insetsCalculation: WindowInsetsHolder.() -> WindowInsets
+): Modifier = composed(inspectorInfo) {
+    val composeInsets = WindowInsetsHolder.current()
+    remember(composeInsets) {
+        val insets = composeInsets.insetsCalculation()
+        InsetsPaddingModifier(insets)
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/WindowInsets.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/WindowInsets.kt
new file mode 100644
index 0000000..627e0f5
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/WindowInsets.kt
@@ -0,0 +1,457 @@
+/*
+ * Copyright 2022 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.foundation.layout
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+
+/**
+ * A representation of window insets that tracks access to enable recomposition,
+ * relayout, and redrawing when values change. These values should not be read during composition
+ * to avoid doing composition for every frame of an animation. Use methods like
+ * [Modifier.windowInsetsPadding], [Modifier.systemBarsPadding], and
+ * [Modifier.windowInsetsTopHeight] for Modifiers that will not cause recomposition when values
+ * change.
+ *
+ * Use the [WindowInsets.Companion] extensions to retrieve [WindowInsets] for the current
+ * window.
+ */
+@Stable
+interface WindowInsets {
+    /**
+     * The space, in pixels, at the left of the window that the inset represents.
+     */
+    fun getLeft(density: Density, layoutDirection: LayoutDirection): Int
+
+    /**
+     * The space, in pixels, at the top of the window that the inset represents.
+     */
+    fun getTop(density: Density): Int
+
+    /**
+     * The space, in pixels, at the right of the window that the inset represents.
+     */
+    fun getRight(density: Density, layoutDirection: LayoutDirection): Int
+
+    /**
+     * The space, in pixels, at the bottom of the window that the inset represents.
+     */
+    fun getBottom(density: Density): Int
+
+    companion object
+}
+
+/**
+ * Returns an [WindowInsets] that has the maximum values of this [WindowInsets] and [insets].
+ */
+fun WindowInsets.union(insets: WindowInsets): WindowInsets = UnionInsets(this, insets)
+
+/**
+ * Returns the values in this [WindowInsets] that are not also in [insets]. For example, if this
+ * [WindowInsets] has a [WindowInsets.getTop] value of `10` and [insets] has a
+ * [WindowInsets.getTop] value of `8`, the returned [WindowInsets] will have a
+ * [WindowInsets.getTop] value of `2`.
+ *
+ * Negative values are never returned. For example if [insets] has a [WindowInsets.getTop] of `10`
+ * and this has a [WindowInsets.getTop] of `0`, the returned [WindowInsets] will have a
+ * [WindowInsets.getTop] value of `0`.
+ */
+fun WindowInsets.exclude(insets: WindowInsets): WindowInsets = ExcludeInsets(this, insets)
+
+/**
+ * Returns the an [WindowInsets] that has values of this, added to the values of [insets].
+ * For example, if this has a top of 10 and insets has a top of 5, the returned [WindowInsets]
+ * will have a top of 15.
+ */
+fun WindowInsets.add(insets: WindowInsets): WindowInsets = AddedInsets(this, insets)
+
+/**
+ * Convert an [WindowInsets] to a [PaddingValues] and uses [LocalDensity] for DP to pixel conversion.
+ * [PaddingValues] can be passed to some containers to pad internal content so that it doesn't
+ * overlap the insets when fully scrolled. Ensure that the insets are [consumed][consumedWindowInsets]
+ * after the padding is applied if insets are to be used further down the hierarchy.
+ *
+ * @sample androidx.compose.foundation.layout.samples.paddingValuesSample
+ */
+@Composable
+fun WindowInsets.asPaddingValues(): PaddingValues = InsetsPaddingValues(this, LocalDensity.current)
+
+/**
+ * Convert a [PaddingValues] to an [WindowInsets].
+ */
+internal fun PaddingValues.asInsets(): WindowInsets = PaddingValuesInsets(this)
+
+/**
+ * Create an [WindowInsets] with fixed dimensions.
+ *
+ * @sample androidx.compose.foundation.layout.samples.insetsInt
+ */
+fun WindowInsets(left: Int = 0, top: Int = 0, right: Int = 0, bottom: Int = 0): WindowInsets =
+    FixedIntInsets(left, top, right, bottom)
+
+/**
+ * Create an [WindowInsets] with fixed dimensions, using [Dp] values.
+ *
+ * @sample androidx.compose.foundation.layout.samples.insetsDp
+ */
+fun WindowInsets(
+    left: Dp = 0.dp,
+    top: Dp = 0.dp,
+    right: Dp = 0.dp,
+    bottom: Dp = 0.dp
+): WindowInsets = FixedDpInsets(left, top, right, bottom)
+
+@Immutable
+private class FixedIntInsets(
+    private val leftVal: Int,
+    private val topVal: Int,
+    private val rightVal: Int,
+    private val bottomVal: Int
+) : WindowInsets {
+    override fun getLeft(density: Density, layoutDirection: LayoutDirection): Int = leftVal
+    override fun getTop(density: Density): Int = topVal
+    override fun getRight(density: Density, layoutDirection: LayoutDirection): Int = rightVal
+    override fun getBottom(density: Density): Int = bottomVal
+
+    override fun toString(): String {
+        return "Insets(left=$leftVal, top=$topVal, right=$rightVal, bottom=$bottomVal)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is FixedIntInsets) {
+            return false
+        }
+
+        return leftVal == other.leftVal && topVal == other.topVal &&
+            rightVal == other.rightVal && bottomVal == other.bottomVal
+    }
+
+    override fun hashCode(): Int {
+        var result = leftVal
+        result = 31 * result + topVal
+        result = 31 * result + rightVal
+        result = 31 * result + bottomVal
+        return result
+    }
+}
+
+@Immutable
+private class FixedDpInsets(
+    private val leftDp: Dp,
+    private val topDp: Dp,
+    private val rightDp: Dp,
+    private val bottomDp: Dp
+) : WindowInsets {
+    override fun getLeft(density: Density, layoutDirection: LayoutDirection) =
+        with(density) { leftDp.roundToPx() }
+
+    override fun getTop(density: Density) = with(density) { topDp.roundToPx() }
+    override fun getRight(density: Density, layoutDirection: LayoutDirection) =
+        with(density) { rightDp.roundToPx() }
+    override fun getBottom(density: Density) = with(density) { bottomDp.roundToPx() }
+
+    override fun toString(): String {
+        return "Insets(left=$leftDp, top=$topDp, right=$rightDp, bottom=$bottomDp)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is FixedDpInsets) {
+            return false
+        }
+
+        return leftDp == other.leftDp && topDp == other.topDp &&
+            rightDp == other.rightDp && bottomDp == other.bottomDp
+    }
+
+    override fun hashCode(): Int {
+        var result = leftDp.hashCode()
+        result = 31 * result + topDp.hashCode()
+        result = 31 * result + rightDp.hashCode()
+        result = 31 * result + bottomDp.hashCode()
+        return result
+    }
+}
+
+/**
+ * An [WindowInsets] that comes straight from [androidx.core.graphics.Insets], whose value can
+ * be updated.
+ */
+@Stable
+internal class ValueInsets(val insets: InsetsValues, val name: String) : WindowInsets {
+    internal var value by mutableStateOf(insets)
+
+    override fun getLeft(density: Density, layoutDirection: LayoutDirection): Int = value.left
+    override fun getTop(density: Density) = value.top
+    override fun getRight(density: Density, layoutDirection: LayoutDirection) = value.right
+    override fun getBottom(density: Density) = value.bottom
+
+    override fun equals(other: Any?): Boolean {
+        if (other === this) {
+            return true
+        }
+        if (other !is ValueInsets) {
+            return false
+        }
+        return value == other.value
+    }
+
+    override fun hashCode(): Int {
+        return name.hashCode()
+    }
+
+    override fun toString(): String {
+        return "$name(left=${insets.left}, top=${insets.top}, " +
+            "right=${insets.right}, bottom=${insets.bottom})"
+    }
+}
+
+@Immutable
+internal class InsetsValues(val left: Int, val top: Int, val right: Int, val bottom: Int) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is InsetsValues) {
+            return false
+        }
+
+        return left == other.left &&
+            top == other.top &&
+            right == other.right &&
+            bottom == other.bottom
+    }
+
+    override fun hashCode(): Int {
+        var result = left
+        result = 31 * result + top
+        result = 31 * result + right
+        result = 31 * result + bottom
+        return result
+    }
+}
+
+/**
+ * An [WindowInsets] that includes the maximum value of [first] and [second] as returned from
+ * [WindowInsets.union].
+ */
+@Stable
+private class UnionInsets(
+    private val first: WindowInsets,
+    private val second: WindowInsets
+) : WindowInsets {
+    override fun getLeft(density: Density, layoutDirection: LayoutDirection) =
+        maxOf(first.getLeft(density, layoutDirection), second.getLeft(density, layoutDirection))
+
+    override fun getTop(density: Density) =
+        maxOf(first.getTop(density), second.getTop(density))
+
+    override fun getRight(density: Density, layoutDirection: LayoutDirection) =
+        maxOf(first.getRight(density, layoutDirection), second.getRight(density, layoutDirection))
+
+    override fun getBottom(density: Density) =
+        maxOf(first.getBottom(density), second.getBottom(density))
+
+    override fun hashCode(): Int = first.hashCode() + second.hashCode() * 31
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is UnionInsets) {
+            return false
+        }
+        return other.first == first && other.second == second
+    }
+
+    override fun toString(): String = "($first ∪ $second)"
+}
+
+/**
+ * An [WindowInsets] that includes the added value of [first] to [second].
+ */
+@Stable
+private class AddedInsets(
+    private val first: WindowInsets,
+    private val second: WindowInsets
+) : WindowInsets {
+    override fun getLeft(density: Density, layoutDirection: LayoutDirection) =
+        first.getLeft(density, layoutDirection) + second.getLeft(density, layoutDirection)
+
+    override fun getTop(density: Density) =
+        first.getTop(density) + second.getTop(density)
+
+    override fun getRight(density: Density, layoutDirection: LayoutDirection) =
+        first.getRight(density, layoutDirection) + second.getRight(density, layoutDirection)
+
+    override fun getBottom(density: Density) =
+        first.getBottom(density) + second.getBottom(density)
+
+    override fun hashCode(): Int = first.hashCode() + second.hashCode() * 31
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is AddedInsets) {
+            return false
+        }
+        return other.first == first && other.second == second
+    }
+
+    override fun toString(): String = "($first + $second)"
+}
+
+/**
+ * An [WindowInsets] that includes the value of [included] that is not included in [excluded] as
+ * returned from [WindowInsets.exclude].
+ */
+@Stable
+private class ExcludeInsets(
+    private val included: WindowInsets,
+    private val excluded: WindowInsets
+) : WindowInsets {
+    override fun getLeft(density: Density, layoutDirection: LayoutDirection) =
+        (included.getLeft(density, layoutDirection) - excluded.getLeft(density, layoutDirection))
+            .coerceAtLeast(0)
+
+    override fun getTop(density: Density) =
+        (included.getTop(density) - excluded.getTop(density)).coerceAtLeast(0)
+
+    override fun getRight(density: Density, layoutDirection: LayoutDirection) =
+        (included.getRight(density, layoutDirection) - excluded.getRight(density, layoutDirection))
+            .coerceAtLeast(0)
+
+    override fun getBottom(density: Density) =
+        (included.getBottom(density) - excluded.getBottom(density)).coerceAtLeast(0)
+
+    override fun toString(): String = "($included - $excluded)"
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is ExcludeInsets) {
+            return false
+        }
+
+        return (other.included == included && other.excluded == excluded)
+    }
+
+    override fun hashCode(): Int = 31 * included.hashCode() + excluded.hashCode()
+}
+
+/**
+ * An [WindowInsets] calculated from [paddingValues].
+ */
+@Stable
+private class PaddingValuesInsets(private val paddingValues: PaddingValues) : WindowInsets {
+    override fun getLeft(density: Density, layoutDirection: LayoutDirection) = with(density) {
+        paddingValues.calculateLeftPadding(layoutDirection).roundToPx()
+    }
+
+    override fun getTop(density: Density) = with(density) {
+        paddingValues.calculateTopPadding().roundToPx()
+    }
+
+    override fun getRight(density: Density, layoutDirection: LayoutDirection) = with(density) {
+        paddingValues.calculateRightPadding(layoutDirection).roundToPx()
+    }
+
+    override fun getBottom(density: Density) = with(density) {
+        paddingValues.calculateBottomPadding().roundToPx()
+    }
+
+    override fun toString(): String {
+        val layoutDirection = LayoutDirection.Ltr
+        val start = paddingValues.calculateLeftPadding(layoutDirection)
+        val top = paddingValues.calculateTopPadding()
+        val end = paddingValues.calculateRightPadding(layoutDirection)
+        val bottom = paddingValues.calculateBottomPadding()
+        return "PaddingValues($start, $top, $end, $bottom)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is PaddingValuesInsets) {
+            return false
+        }
+
+        return other.paddingValues == paddingValues
+    }
+
+    override fun hashCode(): Int = paddingValues.hashCode()
+}
+
+@Stable
+private class InsetsPaddingValues(
+    val insets: WindowInsets,
+    private val density: Density
+) : PaddingValues {
+    override fun calculateLeftPadding(layoutDirection: LayoutDirection) = with(density) {
+        insets.getLeft(this, layoutDirection).toDp()
+    }
+
+    override fun calculateTopPadding() = with(density) {
+        insets.getTop(this).toDp()
+    }
+
+    override fun calculateRightPadding(layoutDirection: LayoutDirection) = with(density) {
+        insets.getRight(this, layoutDirection).toDp()
+    }
+
+    override fun calculateBottomPadding() = with(density) {
+        insets.getBottom(this).toDp()
+    }
+
+    override fun toString(): String {
+        return "InsetsPaddingValues(insets=$insets, density=$density)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is InsetsPaddingValues) {
+            return false
+        }
+        return insets == other.insets && density == other.density
+    }
+
+    override fun hashCode(): Int {
+        var result = insets.hashCode()
+        result = 31 * result + density.hashCode()
+        return result
+    }
+}
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/WindowInsetsPadding.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/WindowInsetsPadding.kt
new file mode 100644
index 0000000..5fbb82fc
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/WindowInsetsPadding.kt
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2022 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.foundation.layout
+
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.LayoutModifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.modifier.ModifierLocalConsumer
+import androidx.compose.ui.modifier.ModifierLocalProvider
+import androidx.compose.ui.modifier.ModifierLocalReadScope
+import androidx.compose.ui.modifier.ProvidableModifierLocal
+import androidx.compose.ui.modifier.modifierLocalOf
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
+import androidx.compose.ui.unit.offset
+
+/**
+ * Adds padding so that the content doesn't enter [insets] space.
+ *
+ * Any insets consumed by other insets padding modifiers or [consumedWindowInsets] on a parent
+ * layout will be excluded from [insets]. [insets] will be [consumed][consumedWindowInsets] for
+ * child layouts as well.
+ *
+ * For example, if an ancestor uses [statusBarsPadding] and this modifier uses
+ * [WindowInsets.Companion.systemBars], the portion of the system bars that the status bars uses
+ * will not be padded again by this modifier.
+ *
+ * @sample androidx.compose.foundation.layout.samples.insetsPaddingSample
+ * @see WindowInsets
+ */
+@Stable
+fun Modifier.windowInsetsPadding(insets: WindowInsets): Modifier = this.then(
+    InsetsPaddingModifier(insets, debugInspectorInfo {
+        name = "windowInsetsPadding"
+        properties["insets"] = insets
+    })
+)
+
+/**
+ * Consume insets that haven't been consumed yet by other insets Modifiers similar to
+ * [windowInsetsPadding] without adding any padding.
+ *
+ * This can be useful when content offsets are provided by [WindowInsets.asPaddingValues].
+ * This should be used further down the hierarchy than the [PaddingValues] is used so
+ * that the values aren't consumed before the padding is added.
+ *
+ * @sample androidx.compose.foundation.layout.samples.consumedInsetsSample
+ */
+@ExperimentalLayoutApi
+@Stable
+fun Modifier.consumedWindowInsets(insets: WindowInsets): Modifier = this.then(
+    UnionInsetsConsumingModifier(insets, debugInspectorInfo {
+        name = "consumedWindowInsets"
+        properties["insets"] = insets
+    })
+)
+
+/**
+ * Consume [paddingValues] as insets as if the padding was added irrespective of insets.
+ * Layouts further down the hierarchy that use [windowInsetsPadding], [safeContentPadding],
+ * and other insets padding Modifiers won't pad for the values that [paddingValues] provides.
+ * This can be useful when content offsets are provided by layout rather than [windowInsetsPadding]
+ * modifiers.
+ *
+ * This method consumes all of [paddingValues] in addition to whatever has been
+ * consumed by other [windowInsetsPadding] modifiers by ancestors. [consumedWindowInsets]
+ * accepting a [WindowInsets] argument ensures that its insets are consumed and doesn't
+ * consume more if they have already been consumed by ancestors.
+ *
+ * @sample androidx.compose.foundation.layout.samples.consumedInsetsPaddingSample
+ */
+@ExperimentalLayoutApi
+@Stable
+fun Modifier.consumedWindowInsets(paddingValues: PaddingValues): Modifier = this.then(
+    PaddingValuesConsumingModifier(paddingValues, debugInspectorInfo {
+        name = "consumedWindowInsets"
+        properties["paddingValues"] = paddingValues
+    })
+)
+
+internal val ModifierLocalConsumedWindowInsets = modifierLocalOf {
+    WindowInsets(0, 0, 0, 0)
+}
+
+internal class InsetsPaddingModifier(
+    private val insets: WindowInsets,
+    inspectorInfo: InspectorInfo.() -> Unit = debugInspectorInfo {
+        name = "InsetsPaddingModifier"
+        properties["insets"] = insets
+    }
+) : InspectorValueInfo(inspectorInfo), LayoutModifier,
+    ModifierLocalConsumer, ModifierLocalProvider<WindowInsets> {
+    private var unconsumedInsets: WindowInsets by mutableStateOf(insets)
+    private var consumedInsets: WindowInsets by mutableStateOf(insets)
+
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        val left = unconsumedInsets.getLeft(this, layoutDirection)
+        val top = unconsumedInsets.getTop(this)
+        val right = unconsumedInsets.getRight(this, layoutDirection)
+        val bottom = unconsumedInsets.getBottom(this)
+
+        val horizontal = left + right
+        val vertical = top + bottom
+
+        val placeable = measurable.measure(constraints.offset(-horizontal, -vertical))
+
+        val width = constraints.constrainWidth(placeable.width + horizontal)
+        val height = constraints.constrainHeight(placeable.height + vertical)
+        return layout(width, height) {
+            placeable.place(left, top)
+        }
+    }
+
+    override fun onModifierLocalsUpdated(scope: ModifierLocalReadScope) {
+        with(scope) {
+            val consumed = ModifierLocalConsumedWindowInsets.current
+            unconsumedInsets = insets.exclude(consumed)
+            consumedInsets = consumed.union(insets)
+        }
+    }
+
+    override val key: ProvidableModifierLocal<WindowInsets>
+        get() = ModifierLocalConsumedWindowInsets
+
+    override val value: WindowInsets
+        get() = consumedInsets
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is InsetsPaddingModifier) {
+            return false
+        }
+
+        return other.insets == insets
+    }
+
+    override fun hashCode(): Int = insets.hashCode()
+}
+
+/**
+ * Base class for arbitrary insets consumption modifiers.
+ */
+@Stable
+private sealed class InsetsConsumingModifier(
+    inspectorInfo: InspectorInfo.() -> Unit
+) : InspectorValueInfo(inspectorInfo), ModifierLocalConsumer, ModifierLocalProvider<WindowInsets> {
+    private var consumedInsets: WindowInsets by mutableStateOf(WindowInsets(0, 0, 0, 0))
+
+    abstract fun calculateInsets(modifierLocalInsets: WindowInsets): WindowInsets
+
+    override fun onModifierLocalsUpdated(scope: ModifierLocalReadScope) {
+        with(scope) {
+            val current = ModifierLocalConsumedWindowInsets.current
+            consumedInsets = calculateInsets(current)
+        }
+    }
+
+    override val key: ProvidableModifierLocal<WindowInsets>
+        get() = ModifierLocalConsumedWindowInsets
+
+    override val value: WindowInsets
+        get() = consumedInsets
+}
+
+@Stable
+private class PaddingValuesConsumingModifier(
+    private val paddingValues: PaddingValues,
+    inspectorInfo: InspectorInfo.() -> Unit
+) : InsetsConsumingModifier(inspectorInfo) {
+    override fun calculateInsets(modifierLocalInsets: WindowInsets): WindowInsets =
+        paddingValues.asInsets().add(modifierLocalInsets)
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is PaddingValuesConsumingModifier) {
+            return false
+        }
+
+        return other.paddingValues == paddingValues
+    }
+
+    override fun hashCode(): Int = paddingValues.hashCode()
+}
+
+@Stable
+private class UnionInsetsConsumingModifier(
+    private val insets: WindowInsets,
+    inspectorInfo: InspectorInfo.() -> Unit
+) : InsetsConsumingModifier(inspectorInfo) {
+    override fun calculateInsets(modifierLocalInsets: WindowInsets): WindowInsets =
+        insets.union(modifierLocalInsets)
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is UnionInsetsConsumingModifier) {
+            return false
+        }
+
+        return other.insets == insets
+    }
+
+    override fun hashCode(): Int = insets.hashCode()
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/WindowInsetsSize.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/WindowInsetsSize.kt
new file mode 100644
index 0000000..59cd2a6
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/WindowInsetsSize.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2022 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.foundation.layout
+
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.LayoutModifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+
+/**
+ * Sets the width to that of [insets] at the [start][androidx.compose.ui.Alignment.Start]
+ * of the screen, using either [left][WindowInsets.getLeft] or [right][WindowInsets.getRight],
+ * depending on the [LayoutDirection].
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.insetsStartWidthSample
+ */
+@Suppress("ModifierInspectorInfo")
+@Stable
+fun Modifier.windowInsetsStartWidth(insets: WindowInsets) = this.then(
+    DerivedWidthModifier(insets, debugInspectorInfo {
+        name = "insetsStartWidth"
+        properties["insets"] = insets
+    }) { layoutDirection, density ->
+        if (layoutDirection == LayoutDirection.Ltr) {
+            getLeft(density, layoutDirection)
+        } else {
+            getRight(density, layoutDirection)
+        }
+    }
+)
+
+/**
+ * Sets the width to that of [insets] at the [end][androidx.compose.ui.Alignment.End]
+ * of the screen, using either [left][WindowInsets.getLeft] or [right][WindowInsets.getRight],
+ * depending on the [LayoutDirection].
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.insetsEndWidthSample
+ */
+@Suppress("ModifierInspectorInfo")
+@Stable
+fun Modifier.windowInsetsEndWidth(insets: WindowInsets) = this.then(
+    DerivedWidthModifier(insets, debugInspectorInfo {
+        name = "insetsEndWidth"
+        properties["insets"] = insets
+    }) { layoutDirection, density ->
+        if (layoutDirection == LayoutDirection.Rtl) {
+            getLeft(density, layoutDirection)
+        } else {
+            getRight(density, layoutDirection)
+        }
+    }
+)
+
+/**
+ * Sets the height to that of [insets] at the [top][WindowInsets.getTop] of the screen.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.insetsTopHeightSample
+ */
+@Suppress("ModifierInspectorInfo")
+@Stable
+fun Modifier.windowInsetsTopHeight(insets: WindowInsets) = this.then(
+    DerivedHeightModifier(insets, debugInspectorInfo {
+        name = "insetsTopHeight"
+        properties["insets"] = insets
+    }) {
+        getTop(it)
+    }
+)
+
+/**
+ * Sets the height to that of [insets] at the [bottom][WindowInsets.getBottom] of the screen.
+ *
+ * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
+ *
+ * @sample androidx.compose.foundation.layout.samples.insetsBottomHeightSample
+ */
+@Suppress("ModifierInspectorInfo")
+@Stable
+fun Modifier.windowInsetsBottomHeight(insets: WindowInsets) = this.then(
+    DerivedHeightModifier(insets, debugInspectorInfo {
+        name = "insetsBottomHeight"
+        properties["insets"] = insets
+    }) {
+        getBottom(it)
+    }
+)
+
+/**
+ * Sets the width based on [widthCalc]. If the width is 0, the height will also always be 0
+ * and the content will not be placed.
+ */
+@Stable
+private class DerivedWidthModifier(
+    private val insets: WindowInsets,
+    inspectorInfo: InspectorInfo.() -> Unit,
+    private val widthCalc: WindowInsets.(LayoutDirection, Density) -> Int
+) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        val width = insets.widthCalc(layoutDirection, this)
+        if (width == 0) {
+            return layout(0, 0) { }
+        }
+        // check for height first
+        val childConstraints = constraints.copy(minWidth = width, maxWidth = width)
+        val placeable = measurable.measure(childConstraints)
+        return layout(width, placeable.height) {
+            placeable.placeRelative(0, 0)
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is DerivedWidthModifier) {
+            return false
+        }
+        return insets == other.insets && widthCalc == other.widthCalc
+    }
+
+    override fun hashCode(): Int = 31 * insets.hashCode() + widthCalc.hashCode()
+}
+
+/**
+ * Sets the height based on [heightCalc]. If the height is 0, the width will also always be 0
+ * and the content will not be placed.
+ */
+@Stable
+private class DerivedHeightModifier(
+    private val insets: WindowInsets,
+    inspectorInfo: InspectorInfo.() -> Unit,
+    private val heightCalc: WindowInsets.(Density) -> Int
+) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        val height = insets.heightCalc(this)
+        if (height == 0) {
+            return layout(0, 0) { }
+        }
+        // check for height first
+        val childConstraints = constraints.copy(minHeight = height, maxHeight = height)
+        val placeable = measurable.measure(childConstraints)
+        return layout(placeable.width, height) {
+            placeable.placeRelative(0, 0)
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other !is DerivedHeightModifier) {
+            return false
+        }
+        return insets == other.insets && heightCalc == other.heightCalc
+    }
+
+    override fun hashCode(): Int = 31 * insets.hashCode() + heightCalc.hashCode()
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/TextFieldToggleTextTestCase.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/TextFieldToggleTextTestCase.kt
index cb891d7..308b085 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/TextFieldToggleTextTestCase.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/TextFieldToggleTextTestCase.kt
@@ -30,7 +30,6 @@
 import androidx.compose.testutils.LayeredComposeTestCase
 import androidx.compose.testutils.ToggleableTestCase
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalTextInputService
 import androidx.compose.ui.text.benchmark.RandomTextGenerator
@@ -101,6 +100,5 @@
         override fun updateState(oldValue: TextFieldValue?, newValue: TextFieldValue) {
             /*do nothing*/
         }
-        override fun notifyFocusedRect(rect: Rect) { /*do nothing*/ }
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyColumnDragAndDropDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyColumnDragAndDropDemo.kt
index 838e3f6..f96933f4 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyColumnDragAndDropDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyColumnDragAndDropDemo.kt
@@ -169,19 +169,11 @@
         val draggingItem = draggingItemLayoutInfo ?: return
         val startOffset = draggingItem.offset + draggingItemOffset
         val endOffset = startOffset + draggingItem.size
+        val middleOffset = startOffset + (endOffset - startOffset) / 2f
 
         val targetItem = state.layoutInfo.visibleItemsInfo.find { item ->
-            if (item.offsetEnd > startOffset && item.offset < endOffset &&
+            middleOffset.toInt() in item.offset..item.offsetEnd &&
                 draggingItem.index != item.index
-            ) {
-                val delta = startOffset - draggingItem.offset
-                when {
-                    delta > 0 -> (endOffset > item.offsetEnd)
-                    else -> (startOffset < item.offset)
-                }
-            } else {
-                false
-            }
         }
         if (targetItem != null) {
             val scrollToIndex = if (targetItem.index == state.firstVisibleItemIndex) {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyGridDragAndDropDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyGridDragAndDropDemo.kt
new file mode 100644
index 0000000..b9c0354
--- /dev/null
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/LazyGridDragAndDropDemo.kt
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2021 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.foundation.demos
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.animation.core.VisibilityThreshold
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.GridCells
+import androidx.compose.foundation.lazy.LazyGridItemInfo
+import androidx.compose.foundation.lazy.LazyGridItemScope
+import androidx.compose.foundation.lazy.LazyGridState
+import androidx.compose.foundation.lazy.LazyVerticalGrid
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.rememberLazyGridState
+import androidx.compose.material.Card
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.pointer.consumeAllChanges
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.toOffset
+import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.zIndex
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.launch
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun LazyGridDragAndDropDemo() {
+    var list by remember { mutableStateOf(List(50) { it }) }
+
+    val gridState = rememberLazyGridState()
+    val dragDropState = rememberGridDragDropState(gridState) { fromIndex, toIndex ->
+        list = list.toMutableList().apply {
+            add(toIndex, removeAt(fromIndex))
+        }
+    }
+
+    LazyVerticalGrid(
+        cells = GridCells.Fixed(3),
+        modifier = Modifier.dragContainer(dragDropState),
+        state = gridState,
+        contentPadding = PaddingValues(16.dp),
+        verticalArrangement = Arrangement.spacedBy(16.dp),
+        horizontalArrangement = Arrangement.spacedBy(16.dp),
+    ) {
+        itemsIndexed(list, key = { _, item -> item }) { index, item ->
+            DraggableItem(dragDropState, index) { isDragging ->
+                val elevation by animateDpAsState(if (isDragging) 4.dp else 1.dp)
+                Card(elevation = elevation) {
+                    Text(
+                        "Item $item",
+                        textAlign = TextAlign.Center,
+                        modifier = Modifier.fillMaxWidth().padding(vertical = 40.dp)
+                    )
+                }
+            }
+        }
+    }
+}
+
+@ExperimentalFoundationApi
+@Composable
+fun rememberGridDragDropState(
+    gridState: LazyGridState,
+    onMove: (Int, Int) -> Unit
+): GridDragDropState {
+    val scope = rememberCoroutineScope()
+    val state = remember(gridState) {
+        GridDragDropState(
+            state = gridState,
+            onMove = onMove,
+            scope = scope
+        )
+    }
+    LaunchedEffect(state) {
+        while (true) {
+            val diff = state.scrollChannel.receive()
+            gridState.scrollBy(diff)
+        }
+    }
+    return state
+}
+
+@ExperimentalFoundationApi
+class GridDragDropState internal constructor(
+    private val state: LazyGridState,
+    private val scope: CoroutineScope,
+    private val onMove: (Int, Int) -> Unit
+) {
+    var draggingItemIndex by mutableStateOf<Int?>(null)
+        private set
+
+    internal val scrollChannel = Channel<Float>()
+
+    private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero)
+    private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
+    internal val draggingItemOffset: Offset
+        get() = draggingItemLayoutInfo?.let { item ->
+            draggingItemInitialOffset + draggingItemDraggedDelta - item.offset.toOffset()
+        } ?: Offset.Zero
+
+    private val draggingItemLayoutInfo: LazyGridItemInfo?
+        get() = state.layoutInfo.visibleItemsInfo
+            .firstOrNull { it.index == draggingItemIndex }
+
+    internal var previousIndexOfDraggedItem by mutableStateOf<Int?>(null)
+        private set
+    internal var previousItemOffset = Animatable(Offset.Zero, Offset.VectorConverter)
+        private set
+
+    internal fun onDragStart(offset: Offset) {
+        state.layoutInfo.visibleItemsInfo
+            .firstOrNull { item ->
+                offset.x.toInt() in item.offset.x..item.offsetEnd.x &&
+                    offset.y.toInt() in item.offset.y..item.offsetEnd.y
+            }?.also {
+                draggingItemIndex = it.index
+                draggingItemInitialOffset = it.offset.toOffset()
+            }
+    }
+
+    internal fun onDragInterrupted() {
+        if (draggingItemIndex != null) {
+            previousIndexOfDraggedItem = draggingItemIndex
+            val startOffset = draggingItemOffset
+            scope.launch {
+                previousItemOffset.snapTo(startOffset)
+                previousItemOffset.animateTo(
+                    Offset.Zero,
+                    spring(
+                        stiffness = Spring.StiffnessMediumLow,
+                        visibilityThreshold = Offset.VisibilityThreshold
+                    )
+                )
+                previousIndexOfDraggedItem = null
+            }
+        }
+        draggingItemDraggedDelta = Offset.Zero
+        draggingItemIndex = null
+        draggingItemInitialOffset = Offset.Zero
+    }
+
+    internal fun onDrag(offset: Offset) {
+        draggingItemDraggedDelta += offset
+
+        val draggingItem = draggingItemLayoutInfo ?: return
+        val startOffset = draggingItem.offset.toOffset() + draggingItemOffset
+        val endOffset = startOffset + draggingItem.size.toSize()
+        val middleOffset = startOffset + (endOffset - startOffset) / 2f
+
+        val targetItem = state.layoutInfo.visibleItemsInfo.find { item ->
+            middleOffset.x.toInt() in item.offset.x..item.offsetEnd.x &&
+                middleOffset.y.toInt() in item.offset.y..item.offsetEnd.y &&
+                draggingItem.index != item.index
+        }
+        if (targetItem != null) {
+            val scrollToIndex = if (targetItem.index == state.firstVisibleItemIndex) {
+                draggingItem.index
+            } else if (draggingItem.index == state.firstVisibleItemIndex) {
+                targetItem.index
+            } else {
+                null
+            }
+            if (scrollToIndex != null) {
+                scope.launch {
+                    // this is needed to neutralize automatic keeping the first item first.
+                    state.scrollToItem(scrollToIndex, state.firstVisibleItemScrollOffset)
+                    onMove.invoke(draggingItem.index, targetItem.index)
+                }
+            } else {
+                onMove.invoke(draggingItem.index, targetItem.index)
+            }
+            draggingItemIndex = targetItem.index
+        } else {
+            val overscroll = when {
+                draggingItemDraggedDelta.y > 0 ->
+                    (endOffset.y - state.layoutInfo.viewportEndOffset).coerceAtLeast(0f)
+                draggingItemDraggedDelta.y < 0 ->
+                    (startOffset.y - state.layoutInfo.viewportStartOffset).coerceAtMost(0f)
+                else -> 0f
+            }
+            if (overscroll != 0f) {
+                scrollChannel.trySend(overscroll)
+            }
+        }
+    }
+
+    private val LazyGridItemInfo.offsetEnd: IntOffset
+        get() = this.offset + this.size
+}
+
+private operator fun IntOffset.plus(size: IntSize): IntOffset {
+    return IntOffset(x + size.width, y + size.height)
+}
+
+private operator fun Offset.plus(size: Size): Offset {
+    return Offset(x + size.width, y + size.height)
+}
+
+@ExperimentalFoundationApi
+fun Modifier.dragContainer(dragDropState: GridDragDropState): Modifier {
+    return pointerInput(dragDropState) {
+        detectDragGesturesAfterLongPress(
+            onDrag = { change, offset ->
+                change.consumeAllChanges()
+                dragDropState.onDrag(offset = offset)
+            },
+            onDragStart = { offset -> dragDropState.onDragStart(offset) },
+            onDragEnd = { dragDropState.onDragInterrupted() },
+            onDragCancel = { dragDropState.onDragInterrupted() }
+        )
+    }
+}
+
+@ExperimentalFoundationApi
+@Composable
+fun LazyGridItemScope.DraggableItem(
+    dragDropState: GridDragDropState,
+    index: Int,
+    modifier: Modifier = Modifier,
+    content: @Composable (isDragging: Boolean) -> Unit
+) {
+    val dragging = index == dragDropState.draggingItemIndex
+    val draggingModifier = if (dragging) {
+        Modifier
+            .zIndex(1f)
+            .graphicsLayer {
+                translationX = dragDropState.draggingItemOffset.x
+                translationY = dragDropState.draggingItemOffset.y
+            }
+    } else if (index == dragDropState.previousIndexOfDraggedItem) {
+        Modifier.zIndex(1f)
+            .graphicsLayer {
+                translationX = dragDropState.previousItemOffset.value.x
+                translationY = dragDropState.previousItemOffset.value.y
+            }
+    } else {
+        Modifier.animateItemPlacement()
+    }
+    Box(modifier = modifier.then(draggingModifier), propagateMinConstraints = true) {
+        content(dragging)
+    }
+}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
index eca4e16..5453664 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
@@ -111,7 +111,8 @@
     ComposableDemo("Custom keys") { ReorderWithCustomKeys() },
     ComposableDemo("Fling Config") { LazyWithFlingConfig() },
     ComposableDemo("Item reordering") { PopularBooksDemo() },
-    ComposableDemo("Drag and drop") { LazyColumnDragAndDropDemo() },
+    ComposableDemo("List drag and drop") { LazyColumnDragAndDropDemo() },
+    ComposableDemo("Grid drag and drop") { LazyGridDragAndDropDemo() },
     PagingDemos
 )
 
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
index 9526465..f82b997 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
@@ -50,6 +50,7 @@
                 ComposableDemo("Inside Dialog") { onNavigateUp ->
                     DialogInputFieldDemo(onNavigateUp)
                 },
+                ComposableDemo("Inside scrollable") { TextFieldsInScrollableDemo() }
             )
         ),
         ComposableDemo("Text Accessibility") { TextAccessibilityDemo() }
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextFieldsInScrollableDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextFieldsInScrollableDemo.kt
new file mode 100644
index 0000000..20deaab
--- /dev/null
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextFieldsInScrollableDemo.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2022 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.foundation.demos.text
+
+import android.app.Activity
+import android.content.ContextWrapper
+import android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
+import android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.Switch
+import androidx.compose.material.Text
+import androidx.compose.material.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+
+private enum class ScrollableType {
+    ScrollableColumn,
+    LazyColumn
+}
+
+@Composable
+fun TextFieldsInScrollableDemo() {
+    var adjustResize by remember { mutableStateOf(false) }
+    var scrollableType by remember { mutableStateOf(ScrollableType.values().first()) }
+
+    @Suppress("DEPRECATION")
+    SoftInputMode(if (adjustResize) SOFT_INPUT_ADJUST_RESIZE else SOFT_INPUT_ADJUST_PAN)
+
+    Column {
+        Row(verticalAlignment = Alignment.CenterVertically) {
+            Text("ADJUST_PAN")
+            Switch(adjustResize, onCheckedChange = { adjustResize = it })
+            Text("ADJUST_RESIZE")
+        }
+        Row(verticalAlignment = Alignment.CenterVertically) {
+            Text("Scrollable column")
+            Switch(
+                checked = scrollableType == ScrollableType.LazyColumn,
+                onCheckedChange = {
+                    scrollableType = if (it) {
+                        ScrollableType.LazyColumn
+                    } else {
+                        ScrollableType.ScrollableColumn
+                    }
+                })
+            Text("LazyColumn")
+        }
+
+        when (scrollableType) {
+            ScrollableType.ScrollableColumn -> TextFieldInScrollableColumn()
+            ScrollableType.LazyColumn -> TextFieldInLazyColumn()
+        }
+    }
+}
+
+@Composable
+fun TextFieldInScrollableColumn() {
+    Column(
+        Modifier.verticalScroll(rememberScrollState())
+    ) {
+        repeat(100) { index ->
+            DemoTextField(index)
+        }
+    }
+}
+
+@Composable
+fun TextFieldInLazyColumn() {
+    LazyColumn {
+        items(100) { index ->
+            DemoTextField(index)
+        }
+    }
+}
+
+@Composable
+private fun DemoTextField(index: Int) {
+    var text by rememberSaveable { mutableStateOf("") }
+    TextField(
+        value = text,
+        onValueChange = { text = it },
+        leadingIcon = { Text(index.toString()) },
+        modifier = Modifier
+            .padding(4.dp)
+            .border(1.dp, Color.Black)
+            .fillMaxWidth()
+    )
+}
+
+/**
+ * Sets the window's [softInputMode][android.view.Window.setSoftInputMode] to [mode] as long as this
+ * function is composed.
+ */
+@Composable
+private fun SoftInputMode(mode: Int) {
+    val context = LocalContext.current
+    DisposableEffect(context, mode) {
+        val activity = generateSequence(context) { (context as? ContextWrapper)?.baseContext }
+            .filterIsInstance<Activity>()
+            .firstOrNull()
+            ?: return@DisposableEffect onDispose {}
+        val originalMode = activity.window.attributes.softInputMode
+        activity.window.setSoftInputMode(mode)
+        onDispose {
+            activity.window.setSoftInputMode(originalMode)
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldKeyboardScrollableInteractionTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldKeyboardScrollableInteractionTest.kt
new file mode 100644
index 0000000..421bdef
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldKeyboardScrollableInteractionTest.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2022 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.foundation.text
+
+import android.app.Activity
+import android.content.Context
+import android.content.ContextWrapper
+import android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
+import android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.text.CoreTextFieldKeyboardScrollableInteractionTest.ScrollableType.LazyList
+import androidx.compose.foundation.text.CoreTextFieldKeyboardScrollableInteractionTest.ScrollableType.ScrollableColumn
+import androidx.compose.foundation.text.CoreTextFieldKeyboardScrollableInteractionTest.SoftInputMode.AdjustPan
+import androidx.compose.foundation.text.CoreTextFieldKeyboardScrollableInteractionTest.SoftInputMode.AdjustResize
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.click
+import androidx.compose.ui.test.isFocused
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.dp
+import org.junit.Assume.assumeTrue
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+@RunWith(Parameterized::class)
+class CoreTextFieldKeyboardScrollableInteractionTest(
+    private val scrollableType: ScrollableType,
+    private val softInputMode: SoftInputMode,
+    private val withDecorationPadding: Boolean,
+) {
+    enum class ScrollableType {
+        ScrollableColumn,
+        LazyList
+    }
+
+    enum class SoftInputMode {
+        AdjustResize,
+        AdjustPan
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "scrollableType={0}, softInputMode={1}, withDecorationPadding={2}")
+        fun parameters(): Iterable<Array<*>> = crossProductOf(
+            ScrollableType.values(),
+            SoftInputMode.values(),
+            arrayOf(false, true), // withDecorationPadding
+        )
+    }
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val ListTag = "list"
+    private val keyboardHelper = KeyboardHelper(rule)
+
+    @Test
+    fun test() {
+        // TODO(b/192043120) This is known broken when soft input mode is Resize.
+        assumeTrue(softInputMode != AdjustResize)
+
+        rule.setContent {
+            keyboardHelper.view = LocalView.current
+            TestContent()
+        }
+
+        // This test is all about the keyboard going from hidden to shown, so hide it to start.
+        keyboardHelper.hideKeyboardIfShown()
+
+        rule.onNodeWithTag(ListTag)
+            .performTouchInput {
+                // Click one pixel above the bottom of the list.
+                click(bottomCenter - Offset(0f, 1f))
+            }
+        keyboardHelper.waitForKeyboardVisibility(visible = true)
+
+        rule.onNode(isFocused()).assertIsDisplayed()
+    }
+
+    @Composable
+    fun TestContent() {
+        @Suppress("DEPRECATION")
+        SoftInputMode(
+            when (softInputMode) {
+                AdjustResize -> SOFT_INPUT_ADJUST_RESIZE
+                AdjustPan -> SOFT_INPUT_ADJUST_PAN
+            }
+        )
+
+        val itemCount = 100
+        when (scrollableType) {
+            ScrollableColumn -> {
+                Column(
+                    Modifier
+                        .testTag(ListTag)
+                        .verticalScroll(rememberScrollState())
+                ) {
+                    repeat(itemCount) { index ->
+                        TestTextField(index)
+                    }
+                }
+            }
+            LazyList -> {
+                LazyColumn(Modifier.testTag(ListTag)) {
+                    items(itemCount) { index ->
+                        TestTextField(index)
+                    }
+                }
+            }
+        }
+    }
+
+    @Composable
+    private fun TestTextField(index: Int) {
+        var isFocused by remember { mutableStateOf(false) }
+        CoreTextField(
+            value = TextFieldValue(text = index.toString()),
+            onValueChange = {},
+            modifier = Modifier
+                .fillMaxWidth()
+                .drawWithContent {
+                    drawContent()
+                    if (isFocused) {
+                        drawRect(Color.Blue, style = Stroke(2.dp.toPx()))
+                    }
+                }
+                .onFocusChanged { isFocused = it.isFocused }
+                .testTag(index.toString()),
+            decorationBox = { inner ->
+                if (withDecorationPadding) {
+                    Box(Modifier.padding(vertical = 24.dp)) {
+                        inner()
+                    }
+                } else {
+                    inner()
+                }
+            }
+        )
+    }
+
+    @Composable
+    private fun SoftInputMode(mode: Int) {
+        val context = LocalContext.current
+        DisposableEffect(mode) {
+            val activity = context.findActivityOrNull() ?: return@DisposableEffect onDispose {}
+            val originalMode = activity.window.attributes.softInputMode
+            activity.window.setSoftInputMode(mode)
+            onDispose {
+                activity.window.setSoftInputMode(originalMode)
+            }
+        }
+    }
+
+    private tailrec fun Context.findActivityOrNull(): Activity? {
+        return (this as? Activity)
+            ?: (this as? ContextWrapper)?.baseContext?.findActivityOrNull()
+    }
+}
+
+private fun crossProductOf(vararg values: Array<*>): List<Array<*>> =
+    crossProductOf(values.map { it.asSequence() })
+        .map { it.toList().toTypedArray() }
+        .toList()
+
+private fun crossProductOf(values: List<Sequence<*>>): Sequence<Sequence<*>> =
+    when (values.size) {
+        0 -> emptySequence()
+        1 -> values[0].map { sequenceOf(it) }
+        else -> sequence {
+            for (subProduct in crossProductOf(values.subList(1, values.size)))
+                for (firstValue in values[0]) {
+                    yield(sequenceOf(firstValue) + subProduct)
+                }
+        }
+    }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldSoftKeyboardTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldSoftKeyboardTest.kt
index ae3ed31..6cdc2c7 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldSoftKeyboardTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldSoftKeyboardTest.kt
@@ -17,10 +17,6 @@
 package androidx.compose.foundation.text
 
 import android.os.Build
-import android.view.View
-import android.view.WindowInsets
-import android.view.WindowInsetsAnimation
-import androidx.annotation.RequiresApi
 import androidx.compose.foundation.layout.Column
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.ExperimentalComposeUiApi
@@ -39,12 +35,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
-import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
 
 @LargeTest
 @RunWith(AndroidJUnit4::class)
@@ -52,9 +45,9 @@
     @get:Rule
     val rule = createComposeRule()
 
-    private lateinit var view: View
     private lateinit var focusManager: FocusManager
     private val timeout = 15_000L
+    private val keyboardHelper = KeyboardHelper(rule, timeout)
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
     @Test
@@ -72,7 +65,7 @@
         rule.onNodeWithTag("TextField1").performClick()
 
         // Assert.
-        view.waitUntil(timeout) { view.isSoftwareKeyboardShown() }
+        keyboardHelper.waitForKeyboardVisibility(visible = true)
     }
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
@@ -92,7 +85,7 @@
         rule.runOnIdle { focusRequester.requestFocus() }
 
         // Assert.
-        view.waitUntil(timeout) { view.isSoftwareKeyboardShown() }
+        keyboardHelper.waitForKeyboardVisibility(visible = true)
     }
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
@@ -109,13 +102,13 @@
         }
         // Request focus and wait for keyboard.
         rule.runOnIdle { focusRequester.requestFocus() }
-        view.waitUntil(timeout) { view.isSoftwareKeyboardShown() }
+        keyboardHelper.waitForKeyboardVisibility(visible = true)
 
         // Act.
         rule.runOnIdle { focusManager.clearFocus() }
 
         // Assert.
-        view.waitUntil(timeout) { !view.isSoftwareKeyboardShown() }
+        keyboardHelper.waitForKeyboardVisibility(visible = false)
     }
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
@@ -123,7 +116,6 @@
     fun keyboardShownAfterDismissingKeyboardAndClickingAgain() {
         // Arrange.
         rule.setContentForTest {
-            view = LocalView.current
             CoreTextField(
                 value = TextFieldValue("Hello"),
                 onValueChange = {},
@@ -131,15 +123,15 @@
             )
         }
         rule.onNodeWithTag("TextField1").performClick()
-        view.waitUntil(timeout) { view.isSoftwareKeyboardShown() }
+        keyboardHelper.waitForKeyboardVisibility(visible = true)
 
         // Act.
-        rule.runOnIdle { view.hideKeyboard() }
-        view.waitUntil(timeout) { !view.isSoftwareKeyboardShown() }
+        rule.runOnIdle { keyboardHelper.hideKeyboard() }
+        keyboardHelper.waitForKeyboardVisibility(visible = false)
         rule.onNodeWithTag("TextField1").performClick()
 
         // Assert.
-        view.waitUntil(timeout) { view.isSoftwareKeyboardShown() }
+        keyboardHelper.waitForKeyboardVisibility(visible = true)
     }
 
     @OptIn(ExperimentalComposeUiApi::class)
@@ -163,64 +155,23 @@
             }
         }
         rule.runOnIdle { focusRequester1.requestFocus() }
-        view.waitUntil(timeout) { view.isSoftwareKeyboardShown() }
+        keyboardHelper.waitForKeyboardVisibility(visible = true)
 
         // Act.
         rule.runOnIdle { focusRequester2.requestFocus() }
 
         // Assert.
-        view.waitUntil(timeout) { !view.isSoftwareKeyboardShown() }
+        keyboardHelper.waitForKeyboardVisibility(visible = false)
     }
 
     private fun ComposeContentTestRule.setContentForTest(composable: @Composable () -> Unit) {
         setContent {
-            view = LocalView.current
+            keyboardHelper.view = LocalView.current
             focusManager = LocalFocusManager.current
             composable()
         }
         // We experienced some flakiness in tests if the keyboard was visible at the start of the
         // test. So we make sure that the keyboard is hidden at the start of every test.
-        runOnIdle {
-            if (view.isSoftwareKeyboardShown()) {
-                view.hideKeyboard()
-                view.waitUntil(timeout) { !view.isSoftwareKeyboardShown() }
-            }
-        }
+        keyboardHelper.hideKeyboardIfShown()
     }
 }
-
-private fun View.waitUntil(timeoutMillis: Long, condition: () -> Boolean) {
-    val latch = CountDownLatch(1)
-    rootView.setWindowInsetsAnimationCallback(
-        InsetAnimationCallback {
-            if (condition()) { latch.countDown() }
-        }
-    )
-    val conditionMet = latch.await(timeoutMillis, TimeUnit.MILLISECONDS)
-    assertThat(conditionMet).isTrue()
-}
-
-@RequiresApi(Build.VERSION_CODES.R)
-private class InsetAnimationCallback(val block: () -> Unit) :
-    WindowInsetsAnimation.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
-
-    override fun onProgress(
-        insets: WindowInsets,
-        runningAnimations: MutableList<WindowInsetsAnimation>
-    ) = insets
-
-    override fun onEnd(animation: WindowInsetsAnimation) {
-        block()
-        super.onEnd(animation)
-    }
-}
-
-@RequiresApi(Build.VERSION_CODES.R)
-private fun View.isSoftwareKeyboardShown(): Boolean {
-    return rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsets.Type.ime())
-}
-
-@RequiresApi(Build.VERSION_CODES.R)
-private fun View.hideKeyboard() {
-    windowInsetsController?.hide(WindowInsets.Type.ime())
-}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/KeyboardHelper.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/KeyboardHelper.kt
new file mode 100644
index 0000000..87fe0e8
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/KeyboardHelper.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2022 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.foundation.text
+
+import android.content.Context
+import android.os.Build
+import android.view.View
+import android.view.WindowInsets
+import android.view.WindowInsetsAnimation
+import android.view.inputmethod.InputMethodManager
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.test.junit4.ComposeTestRule
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+/**
+ * Helper methods for hiding and showing the keyboard in tests.
+ * Must set [view] before calling any methods on this class.
+ */
+class KeyboardHelper(
+    private val composeRule: ComposeTestRule,
+    private val timeout: Long = 15_000L
+) {
+    /**
+     * The [View] hosting the compose rule's content. Must be set before calling any methods on this
+     * class.
+     */
+    lateinit var view: View
+    private val imm by lazy {
+        view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+    }
+
+    /**
+     * Requests the keyboard to be hidden without waiting for it.
+     * Should be called from the main thread.
+     */
+    fun hideKeyboard() {
+        if (Build.VERSION.SDK_INT >= 30) {
+            hideKeyboardWithInsets()
+        } else {
+            hideKeyboardWithImm()
+        }
+    }
+
+    /**
+     * Blocks until the [timeout] or the keyboard's visibility matches [visible].
+     * May be called from the test thread or the main thread.
+     */
+    fun waitForKeyboardVisibility(visible: Boolean) {
+        waitUntil(timeout) {
+            isSoftwareKeyboardShown() == visible
+        }
+    }
+
+    fun hideKeyboardIfShown() {
+        composeRule.runOnIdle {
+            if (isSoftwareKeyboardShown()) {
+                hideKeyboard()
+                waitForKeyboardVisibility(visible = false)
+            }
+        }
+    }
+
+    private fun isSoftwareKeyboardShown(): Boolean {
+        return if (Build.VERSION.SDK_INT >= 30) {
+            isSoftwareKeyboardShownWithInsets()
+        } else {
+            isSoftwareKeyboardShownWithImm()
+        }
+    }
+
+    @RequiresApi(30)
+    private fun isSoftwareKeyboardShownWithInsets(): Boolean {
+        return view.rootWindowInsets != null &&
+            view.rootWindowInsets.isVisible(WindowInsets.Type.ime())
+    }
+
+    private fun isSoftwareKeyboardShownWithImm(): Boolean {
+        // TODO(b/163742556): This is just a proxy for software keyboard visibility. Find a better
+        //  way to check if the software keyboard is shown.
+        return imm.isAcceptingText
+    }
+
+    private fun hideKeyboardWithImm() {
+        imm.hideSoftInputFromWindow(view.windowToken, 0)
+    }
+
+    @RequiresApi(30)
+    private fun hideKeyboardWithInsets() {
+        view.windowInsetsController?.hide(WindowInsets.Type.ime())
+    }
+
+    private fun waitUntil(timeout: Long, condition: () -> Boolean) {
+        if (Build.VERSION.SDK_INT >= 30) {
+            view.waitUntil(timeout, condition)
+        } else {
+            composeRule.waitUntil(timeout, condition)
+        }
+    }
+}
+
+@RequiresApi(30)
+fun View.waitUntil(timeoutMillis: Long, condition: () -> Boolean) {
+    val latch = CountDownLatch(1)
+    rootView.setWindowInsetsAnimationCallback(
+        InsetAnimationCallback {
+            if (condition()) {
+                latch.countDown()
+            }
+        }
+    )
+    val conditionMet = latch.await(timeoutMillis, TimeUnit.MILLISECONDS)
+    assertThat(conditionMet).isTrue()
+}
+
+@RequiresApi(30)
+private class InsetAnimationCallback(val block: () -> Unit) :
+    WindowInsetsAnimation.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
+
+    override fun onProgress(
+        insets: WindowInsets,
+        runningAnimations: MutableList<WindowInsetsAnimation>
+    ) = insets
+
+    override fun onEnd(animation: WindowInsetsAnimation) {
+        block()
+        super.onEnd(animation)
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
index 67e42d6..ff69b33 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
@@ -62,6 +62,17 @@
     val scope = rememberCoroutineScope()
     val focusedInteraction = remember { mutableStateOf<FocusInteraction.Focus?>(null) }
     var isFocused by remember { mutableStateOf(false) }
+
+    // Focusables have a few different cases where they need to make sure they stay visible:
+    //
+    // 1. Focusable node newly receives focus – always bring entire node into view. That's what this
+    //    BringIntoViewRequester does.
+    // 2. Scrollable parent resizes and the currently-focused item is now hidden – bring entire node
+    //    into view if it was also in view before the resize. This handles the case of
+    //    `softInputMode=ADJUST_RESIZE`. See b/216842427.
+    // 3. Entire window is panned due to `softInputMode=ADJUST_PAN` – report the correct focused
+    //    rect to the view system, and the view system itself will keep the focused area in view.
+    //    See aosp/1964580.
     @OptIn(ExperimentalFoundationApi::class)
     val bringIntoViewRequester = remember { BringIntoViewRequester() }
     DisposableEffect(interactionSource) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
index 348fa20..aabc044 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
@@ -16,10 +16,13 @@
 
 package androidx.compose.foundation.text
 
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.relocation.BringIntoViewRequester
+import androidx.compose.foundation.relocation.bringIntoViewRequester
 import androidx.compose.foundation.text.selection.LocalTextSelectionColors
 import androidx.compose.foundation.text.selection.SelectionHandleInfo
 import androidx.compose.foundation.text.selection.SelectionHandleInfoKey
@@ -35,6 +38,7 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
@@ -42,6 +46,7 @@
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Paint
@@ -100,6 +105,7 @@
 import androidx.compose.ui.unit.Density
 import kotlin.math.max
 import kotlin.math.roundToInt
+import kotlinx.coroutines.launch
 
 /**
  * Base composable that enables users to edit text via hardware or software keyboard.
@@ -163,7 +169,7 @@
  * innerTextField exactly once.
  */
 @Composable
-@OptIn(InternalFoundationTextApi::class)
+@OptIn(InternalFoundationTextApi::class, ExperimentalFoundationApi::class)
 internal fun CoreTextField(
     value: TextFieldValue,
     onValueChange: (TextFieldValue) -> Unit,
@@ -256,6 +262,9 @@
     manager.focusRequester = focusRequester
     manager.editable = !readOnly
 
+    val coroutineScope = rememberCoroutineScope()
+    val bringIntoViewRequester = remember { BringIntoViewRequester() }
+
     // Focus
     val focusModifier = Modifier.textFieldFocusModifier(
         enabled = enabled,
@@ -272,9 +281,28 @@
                 textInputService,
                 state,
                 value,
-                imeOptions,
-                offsetMapping
+                imeOptions
             )
+
+            // The focusable modifier itself will request the entire focusable be brought into view
+            // when it gains focus – in this case, that's the decoration box. However, since text
+            // fields may have their own internal scrolling, and the decoration box can do anything,
+            // we also need to specifically request that the cursor itself be brought into view.
+            // TODO(b/216790855) If this request happens after the focusable's request, the field
+            //  will only be scrolled far enough to show the cursor, _not_ the entire decoration
+            //  box.
+            if (it.isFocused) {
+                state.layoutResult?.let { layoutResult ->
+                    coroutineScope.launch {
+                        bringIntoViewRequester.bringSelectionEndIntoView(
+                            value,
+                            state.textDelegate,
+                            layoutResult.value,
+                            offsetMapping
+                        )
+                    }
+                }
+            }
         }
         if (!it.isFocused) manager.deselect()
     }
@@ -342,19 +370,6 @@
                 state.showCursorHandle =
                     manager.isSelectionHandleInVisibleBound(isStartHandle = true)
             }
-            state.layoutResult?.let { layoutResult ->
-                state.inputSession?.let { inputSession ->
-                    TextFieldDelegate.notifyFocusedRect(
-                        value,
-                        state.textDelegate,
-                        layoutResult.value,
-                        it,
-                        inputSession,
-                        state.hasFocus,
-                        offsetMapping
-                    )
-                }
-            }
         }
         state.layoutResult?.innerTextFieldCoordinates = it
     }
@@ -520,6 +535,7 @@
                 .textFieldMinSize(textStyle)
                 .then(onPositionedModifier)
                 .then(magnifierModifier)
+                .bringIntoViewRequester(bringIntoViewRequester)
 
             SimpleLayout(coreTextFieldModifier) {
                 Layout(
@@ -789,13 +805,12 @@
     }
 }
 
-@OptIn(InternalFoundationTextApi::class)
+@OptIn(InternalFoundationTextApi::class, ExperimentalFoundationApi::class)
 private fun notifyTextInputServiceOnFocusChange(
     textInputService: TextInputService,
     state: TextFieldState,
     value: TextFieldValue,
-    imeOptions: ImeOptions,
-    offsetMapping: OffsetMapping
+    imeOptions: ImeOptions
 ) {
     if (state.hasFocus) {
         state.inputSession = TextFieldDelegate.onFocus(
@@ -805,21 +820,7 @@
             imeOptions,
             state.onValueChange,
             state.onImeActionPerformed
-        ).also { newSession ->
-            state.layoutCoordinates?.let { coords ->
-                state.layoutResult?.let { layoutResult ->
-                    TextFieldDelegate.notifyFocusedRect(
-                        value,
-                        state.textDelegate,
-                        layoutResult.value,
-                        coords,
-                        newSession,
-                        state.hasFocus,
-                        offsetMapping
-                    )
-                }
-            }
-        }
+        )
     } else {
         state.inputSession?.let { session ->
             TextFieldDelegate.onBlur(session, state.processor, state.onValueChange)
@@ -828,6 +829,54 @@
     }
 }
 
+/**
+ * Calculates the location of the end of the current selection and requests that it be brought into
+ * view using [bringIntoView][BringIntoViewRequester.bringIntoView].
+ *
+ * Text fields have a lot of different edge cases where they need to make sure they stay visible:
+ *
+ * 1. Focusable node newly receives focus – always bring entire node into view.
+ * 2. Unfocused text field is tapped – always bring cursor area into view (conflicts with above, see
+ *    b/216790855).
+ * 3. Focused text field is tapped – always bring cursor area into view.
+ * 4. Text input occurs – always bring cursor area into view.
+ * 5. Scrollable parent resizes and the currently-focused item is now hidden – bring entire node
+ *    into view if it was also in view before the resize. This handles the case of
+ *    `softInputMode=ADJUST_RESIZE`. See b/216842427.
+ * 6. Entire window is panned due to `softInputMode=ADJUST_PAN` – report the correct focused rect to
+ *    the view system, and the view system itself will keep the focused area in view.
+ *    See aosp/1964580.
+ *
+ * This function is used to handle 2, 3, and 4, and the others are automatically handled by the
+ * focus system.
+ */
+@OptIn(ExperimentalFoundationApi::class, InternalFoundationTextApi::class)
+internal suspend fun BringIntoViewRequester.bringSelectionEndIntoView(
+    value: TextFieldValue,
+    textDelegate: TextDelegate,
+    textLayoutResult: TextLayoutResult,
+    offsetMapping: OffsetMapping
+) {
+    val selectionEndInTransformed = offsetMapping.originalToTransformed(value.selection.max)
+    val selectionEndBounds = when {
+        selectionEndInTransformed < textLayoutResult.layoutInput.text.length -> {
+            textLayoutResult.getBoundingBox(selectionEndInTransformed)
+        }
+        selectionEndInTransformed != 0 -> {
+            textLayoutResult.getBoundingBox(selectionEndInTransformed - 1)
+        }
+        else -> { // empty text.
+            val defaultSize = computeSizeForDefaultText(
+                textDelegate.style,
+                textDelegate.density,
+                textDelegate.fontFamilyResolver
+            )
+            Rect(0f, 0f, 1.0f, defaultSize.height.toFloat())
+        }
+    }
+    bringIntoView(selectionEndBounds)
+}
+
 @Composable
 private fun SelectionToolbarAndHandles(manager: TextFieldSelectionManager, show: Boolean) {
     if (show) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
index 2f001a7..c892fcc 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
@@ -17,11 +17,8 @@
 package androidx.compose.foundation.text
 
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Paint
-import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.Paragraph
 import androidx.compose.ui.text.SpanStyle
@@ -134,55 +131,6 @@
         }
 
         /**
-         * Notify system that focused input area.
-         *
-         * System is typically scrolled up not to be covered by keyboard.
-         *
-         * @param value The editor model
-         * @param textDelegate The text delegate
-         * @param layoutCoordinates The layout coordinates
-         * @param textInputSession The current input session.
-         * @param hasFocus True if focus is gained.
-         * @param offsetMapping The mapper from/to editing buffer to/from visible text.
-         */
-        @JvmStatic
-        internal fun notifyFocusedRect(
-            value: TextFieldValue,
-            textDelegate: TextDelegate,
-            textLayoutResult: TextLayoutResult,
-            layoutCoordinates: LayoutCoordinates,
-            textInputSession: TextInputSession,
-            hasFocus: Boolean,
-            offsetMapping: OffsetMapping
-        ) {
-            if (!hasFocus) {
-                return
-            }
-            val focusOffsetInTransformed = offsetMapping.originalToTransformed(value.selection.max)
-            val bbox = when {
-                focusOffsetInTransformed < textLayoutResult.layoutInput.text.length -> {
-                    textLayoutResult.getBoundingBox(focusOffsetInTransformed)
-                }
-                focusOffsetInTransformed != 0 -> {
-                    textLayoutResult.getBoundingBox(focusOffsetInTransformed - 1)
-                }
-                else -> { // empty text.
-                    val defaultSize = computeSizeForDefaultText(
-                        textDelegate.style,
-                        textDelegate.density,
-                        textDelegate.fontFamilyResolver
-                    )
-                    Rect(0f, 0f, 1.0f, defaultSize.height.toFloat())
-                }
-            }
-            val globalLT = layoutCoordinates.localToRoot(Offset(bbox.left, bbox.top))
-
-            textInputSession.notifyFocusedRect(
-                Rect(Offset(globalLT.x, globalLT.y), Size(bbox.width, bbox.height))
-            )
-        }
-
-        /**
          * Called when edit operations are passed from TextInputService
          *
          * @param ops A list of edit operations.
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldBringIntoViewTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldBringIntoViewTest.kt
new file mode 100644
index 0000000..9296223
--- /dev/null
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldBringIntoViewTest.kt
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2022 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.foundation.text
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.relocation.BringIntoViewRequester
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.layout.AlignmentLine
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextLayoutInput
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.OffsetMapping
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import com.nhaarman.mockitokotlin2.any
+import com.nhaarman.mockitokotlin2.mock
+import com.nhaarman.mockitokotlin2.reset
+import com.nhaarman.mockitokotlin2.verify
+import com.nhaarman.mockitokotlin2.verifyBlocking
+import com.nhaarman.mockitokotlin2.whenever
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(InternalFoundationTextApi::class, ExperimentalFoundationApi::class)
+@RunWith(JUnit4::class)
+class TextFieldBringIntoViewTest {
+
+    private val delegate: TextDelegate = mock()
+    private var layoutCoordinates: LayoutCoordinates = mock()
+    private val textLayoutResultProxy: TextLayoutResultProxy = mock()
+    private val textLayoutResult: TextLayoutResult = mock()
+    private val bringIntoViewRequester: BringIntoViewRequester = mock()
+
+    /**
+     * Test implementation of offset map which doubles the offset in transformed text.
+     */
+    private val skippingOffsetMap = object : OffsetMapping {
+        override fun originalToTransformed(offset: Int): Int = offset * 2
+        override fun transformedToOriginal(offset: Int): Int = offset / 2
+    }
+
+    @Before
+    fun setup() {
+        whenever(textLayoutResultProxy.value).thenReturn(textLayoutResult)
+    }
+
+    @Test
+    fun notify_focused_rect() {
+        val rect = Rect(0f, 1f, 2f, 3f)
+        whenever(textLayoutResult.getBoundingBox(any())).thenReturn(rect)
+        val point = Offset(5f, 6f)
+        layoutCoordinates = MockCoordinates(
+            rootOffset = point
+        )
+        val editorState = TextFieldValue(text = "Hello, World", selection = TextRange(1))
+
+        val input = TextLayoutInput(
+            text = AnnotatedString(editorState.text),
+            style = TextStyle(),
+            placeholders = listOf(),
+            maxLines = Int.MAX_VALUE,
+            softWrap = true,
+            overflow = TextOverflow.Clip,
+            density = Density(1.0f),
+            layoutDirection = LayoutDirection.Ltr,
+            fontFamilyResolver = mock(),
+            constraints = mock()
+        )
+        whenever(textLayoutResult.layoutInput).thenReturn(input)
+
+        runBlocking {
+            bringIntoViewRequester.bringSelectionEndIntoView(
+                editorState,
+                delegate,
+                textLayoutResult,
+                OffsetMapping.Identity
+            )
+        }
+        verifyBlocking(bringIntoViewRequester) { bringIntoView(rect) }
+    }
+
+    @Test
+    fun notify_rect_tail() {
+        val rect = Rect(0f, 1f, 2f, 3f)
+        whenever(textLayoutResult.getBoundingBox(any())).thenReturn(rect)
+        val point = Offset(5f, 6f)
+        layoutCoordinates = MockCoordinates(
+            rootOffset = point
+        )
+        val editorState = TextFieldValue(text = "Hello, World", selection = TextRange(12))
+        val input = TextLayoutInput(
+            text = AnnotatedString(editorState.text),
+            style = TextStyle(),
+            placeholders = listOf(),
+            maxLines = Int.MAX_VALUE,
+            softWrap = true,
+            overflow = TextOverflow.Clip,
+            density = Density(1.0f),
+            layoutDirection = LayoutDirection.Ltr,
+            fontFamilyResolver = mock(),
+            constraints = mock()
+        )
+        whenever(textLayoutResult.layoutInput).thenReturn(input)
+
+        runBlocking {
+            bringIntoViewRequester.bringSelectionEndIntoView(
+                editorState,
+                delegate,
+                textLayoutResult,
+                OffsetMapping.Identity
+            )
+        }
+        verifyBlocking(bringIntoViewRequester) { bringIntoView(rect) }
+    }
+
+    @Test
+    fun check_notify_rect_uses_offset_map() {
+        val rect = Rect(0f, 1f, 2f, 3f)
+        val point = Offset(5f, 6f)
+        val editorState = TextFieldValue(text = "Hello, World", selection = TextRange(1, 3))
+
+        whenever(textLayoutResult.getBoundingBox(any())).thenReturn(rect)
+        val input = TextLayoutInput(
+            text = AnnotatedString(editorState.text),
+            style = TextStyle(),
+            placeholders = listOf(),
+            maxLines = Int.MAX_VALUE,
+            softWrap = true,
+            overflow = TextOverflow.Clip,
+            density = Density(1.0f),
+            layoutDirection = LayoutDirection.Ltr,
+            fontFamilyResolver = mock(),
+            constraints = mock()
+        )
+        whenever(textLayoutResult.layoutInput).thenReturn(input)
+        layoutCoordinates = MockCoordinates(
+            rootOffset = point
+        )
+
+        runBlocking {
+            bringIntoViewRequester.bringSelectionEndIntoView(
+                editorState,
+                delegate,
+                textLayoutResult,
+                skippingOffsetMap
+            )
+        }
+        verify(textLayoutResult).getBoundingBox(6)
+        verifyBlocking(bringIntoViewRequester) { bringIntoView(rect) }
+    }
+
+    @Test
+    fun notify_transformed_text() {
+        val rect = Rect(0f, 1f, 2f, 3f)
+        whenever(textLayoutResult.getBoundingBox(any())).thenReturn(rect)
+        val point = Offset(5f, 6f)
+        layoutCoordinates = MockCoordinates(
+            rootOffset = point
+        )
+
+        val input = TextLayoutInput(
+            // In this test case, transform the text into double characters text.
+            text = AnnotatedString("HHeelllloo,,  WWoorrlldd"),
+            style = TextStyle(),
+            placeholders = listOf(),
+            maxLines = Int.MAX_VALUE,
+            softWrap = true,
+            overflow = TextOverflow.Clip,
+            density = Density(1.0f),
+            layoutDirection = LayoutDirection.Ltr,
+            fontFamilyResolver = mock(),
+            constraints = mock()
+        )
+        whenever(textLayoutResult.layoutInput).thenReturn(input)
+
+        val offsetMapping = object : OffsetMapping {
+            override fun originalToTransformed(offset: Int): Int = offset * 2
+            override fun transformedToOriginal(offset: Int): Int = offset / 2
+        }
+
+        // The beginning of the text.
+        runBlocking {
+            bringIntoViewRequester.bringSelectionEndIntoView(
+                TextFieldValue(text = "Hello, World", selection = TextRange(0)),
+                delegate,
+                textLayoutResult,
+                offsetMapping
+            )
+        }
+        verifyBlocking(bringIntoViewRequester) { bringIntoView(rect) }
+
+        // The tail of the transformed text.
+        reset(bringIntoViewRequester)
+        runBlocking {
+            bringIntoViewRequester.bringSelectionEndIntoView(
+                TextFieldValue(text = "Hello, World", selection = TextRange(24)),
+                delegate,
+                textLayoutResult,
+                offsetMapping
+            )
+        }
+        verifyBlocking(bringIntoViewRequester) { bringIntoView(rect) }
+
+        // Beyond the tail of the transformed text.
+        reset(bringIntoViewRequester)
+        runBlocking {
+            bringIntoViewRequester.bringSelectionEndIntoView(
+                TextFieldValue(text = "Hello, World", selection = TextRange(25)),
+                delegate,
+                textLayoutResult,
+                offsetMapping
+            )
+        }
+        verifyBlocking(bringIntoViewRequester) { bringIntoView(rect) }
+    }
+
+    private class MockCoordinates(
+        override val size: IntSize = IntSize.Zero,
+        val localOffset: Offset = Offset.Zero,
+        val globalOffset: Offset = Offset.Zero,
+        val rootOffset: Offset = Offset.Zero
+    ) : LayoutCoordinates {
+        override val providedAlignmentLines: Set<AlignmentLine>
+            get() = emptySet()
+        override val parentLayoutCoordinates: LayoutCoordinates?
+            get() = null
+        override val parentCoordinates: LayoutCoordinates?
+            get() = null
+        override val isAttached: Boolean
+            get() = true
+
+        override fun windowToLocal(relativeToWindow: Offset): Offset = localOffset
+
+        override fun localToWindow(relativeToLocal: Offset): Offset = globalOffset
+
+        override fun localToRoot(relativeToLocal: Offset): Offset = rootOffset
+        override fun localPositionOf(
+            sourceCoordinates: LayoutCoordinates,
+            relativeToSource: Offset
+        ): Offset = Offset.Zero
+
+        override fun localBoundingBoxOf(
+            sourceCoordinates: LayoutCoordinates,
+            clipBounds: Boolean
+        ): Rect = Rect.Zero
+
+        override fun get(alignmentLine: AlignmentLine): Int = 0
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
index 6ec1a8a..d586f8f 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
@@ -17,18 +17,14 @@
 package androidx.compose.foundation.text
 
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.AlignmentLine
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.MultiParagraphIntrinsics
 import androidx.compose.ui.text.SpanStyle
-import androidx.compose.ui.text.TextLayoutInput
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextRange
-import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.EditProcessor
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.ImeOptions
@@ -41,17 +37,11 @@
 import androidx.compose.ui.text.input.TransformedText
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.text.style.TextDecoration
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
 import com.google.common.truth.Truth.assertThat
 import com.nhaarman.mockitokotlin2.any
 import com.nhaarman.mockitokotlin2.eq
 import com.nhaarman.mockitokotlin2.inOrder
 import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.never
-import com.nhaarman.mockitokotlin2.reset
 import com.nhaarman.mockitokotlin2.times
 import com.nhaarman.mockitokotlin2.verify
 import com.nhaarman.mockitokotlin2.whenever
@@ -184,133 +174,6 @@
     }
 
     @Test
-    fun notify_focused_rect() {
-        val rect = Rect(0f, 1f, 2f, 3f)
-        whenever(textLayoutResult.getBoundingBox(any())).thenReturn(rect)
-        val point = Offset(5f, 6f)
-        layoutCoordinates = MockCoordinates(
-            rootOffset = point
-        )
-        val editorState = TextFieldValue(text = "Hello, World", selection = TextRange(1))
-        val textInputSession: TextInputSession = mock()
-
-        val input = TextLayoutInput(
-            text = AnnotatedString(editorState.text),
-            style = TextStyle(),
-            placeholders = listOf(),
-            maxLines = Int.MAX_VALUE,
-            softWrap = true,
-            overflow = TextOverflow.Clip,
-            density = Density(1.0f),
-            layoutDirection = LayoutDirection.Ltr,
-            fontFamilyResolver = mock(),
-            constraints = mock()
-        )
-        whenever(textLayoutResult.layoutInput).thenReturn(input)
-
-        TextFieldDelegate.notifyFocusedRect(
-            editorState,
-            mDelegate,
-            textLayoutResult,
-            layoutCoordinates,
-            textInputSession,
-            true /* hasFocus */,
-            OffsetMapping.Identity
-        )
-        verify(textInputSession).notifyFocusedRect(any())
-    }
-
-    @Test
-    fun notify_focused_rect_without_focus() {
-        val editorState = TextFieldValue(text = "Hello, World", selection = TextRange(1))
-        val textInputSession: TextInputSession = mock()
-        TextFieldDelegate.notifyFocusedRect(
-            editorState,
-            mDelegate,
-            textLayoutResult,
-            layoutCoordinates,
-            textInputSession,
-            false /* hasFocus */,
-            OffsetMapping.Identity
-        )
-        verify(textInputSession, never()).notifyFocusedRect(any())
-    }
-
-    @Test
-    fun notify_rect_tail() {
-        val rect = Rect(0f, 1f, 2f, 3f)
-        whenever(textLayoutResult.getBoundingBox(any())).thenReturn(rect)
-        val point = Offset(5f, 6f)
-        layoutCoordinates = MockCoordinates(
-            rootOffset = point
-        )
-        val editorState = TextFieldValue(text = "Hello, World", selection = TextRange(12))
-        val textInputSession: TextInputSession = mock()
-        val input = TextLayoutInput(
-            text = AnnotatedString(editorState.text),
-            style = TextStyle(),
-            placeholders = listOf(),
-            maxLines = Int.MAX_VALUE,
-            softWrap = true,
-            overflow = TextOverflow.Clip,
-            density = Density(1.0f),
-            layoutDirection = LayoutDirection.Ltr,
-            fontFamilyResolver = mock(),
-            constraints = mock()
-        )
-        whenever(textLayoutResult.layoutInput).thenReturn(input)
-
-        TextFieldDelegate.notifyFocusedRect(
-            editorState,
-            mDelegate,
-            textLayoutResult,
-            layoutCoordinates,
-            textInputSession,
-            true /* hasFocus */,
-            OffsetMapping.Identity
-        )
-        verify(textInputSession).notifyFocusedRect(any())
-    }
-
-    @Test
-    fun check_notify_rect_uses_offset_map() {
-        val rect = Rect(0f, 1f, 2f, 3f)
-        val point = Offset(5f, 6f)
-        val editorState = TextFieldValue(text = "Hello, World", selection = TextRange(1, 3))
-
-        whenever(textLayoutResult.getBoundingBox(any())).thenReturn(rect)
-        val input = TextLayoutInput(
-            text = AnnotatedString(editorState.text),
-            style = TextStyle(),
-            placeholders = listOf(),
-            maxLines = Int.MAX_VALUE,
-            softWrap = true,
-            overflow = TextOverflow.Clip,
-            density = Density(1.0f),
-            layoutDirection = LayoutDirection.Ltr,
-            fontFamilyResolver = mock(),
-            constraints = mock()
-        )
-        whenever(textLayoutResult.layoutInput).thenReturn(input)
-        layoutCoordinates = MockCoordinates(
-            rootOffset = point
-        )
-        val textInputSession: TextInputSession = mock()
-
-        TextFieldDelegate.notifyFocusedRect(
-            editorState,
-            mDelegate,
-            textLayoutResult,
-            layoutCoordinates,
-            textInputSession,
-            true /* hasFocus */,
-            skippingOffsetMap
-        )
-        verify(textLayoutResult).getBoundingBox(6)
-        verify(textInputSession).notifyFocusedRect(any())
-    }
-
-    @Test
     fun check_setCursorOffset_uses_offset_map() {
         val position = Offset(100f, 200f)
         val offset = 10
@@ -405,106 +268,4 @@
             )
         )
     }
-
-    @Test
-    fun notify_transformed_text() {
-        val rect = Rect(0f, 1f, 2f, 3f)
-        whenever(textLayoutResult.getBoundingBox(any())).thenReturn(rect)
-        val point = Offset(5f, 6f)
-        layoutCoordinates = MockCoordinates(
-            rootOffset = point
-        )
-
-        val textInputSession: TextInputSession = mock()
-        val input = TextLayoutInput(
-            // In this test case, transform the text into double characters text.
-            text = AnnotatedString("HHeelllloo,,  WWoorrlldd"),
-            style = TextStyle(),
-            placeholders = listOf(),
-            maxLines = Int.MAX_VALUE,
-            softWrap = true,
-            overflow = TextOverflow.Clip,
-            density = Density(1.0f),
-            layoutDirection = LayoutDirection.Ltr,
-            fontFamilyResolver = mock(),
-            constraints = mock()
-        )
-        whenever(textLayoutResult.layoutInput).thenReturn(input)
-
-        val offsetMapping = object : OffsetMapping {
-            override fun originalToTransformed(offset: Int): Int = offset * 2
-            override fun transformedToOriginal(offset: Int): Int = offset / 2
-        }
-
-        // The beginning of the text.
-        TextFieldDelegate.notifyFocusedRect(
-            TextFieldValue(text = "Hello, World", selection = TextRange(0)),
-            mDelegate,
-            textLayoutResult,
-            layoutCoordinates,
-            textInputSession,
-            true /* hasFocus */,
-            offsetMapping
-        )
-        verify(textInputSession).notifyFocusedRect(any())
-
-        // The tail of the transformed text.
-        reset(textInputSession)
-        TextFieldDelegate.notifyFocusedRect(
-            TextFieldValue(text = "Hello, World", selection = TextRange(24)),
-            mDelegate,
-            textLayoutResult,
-            layoutCoordinates,
-            textInputSession,
-            true /* hasFocus */,
-            offsetMapping
-        )
-        verify(textInputSession).notifyFocusedRect(any())
-
-        // Beyond the tail of the transformed text.
-        reset(textInputSession)
-        TextFieldDelegate.notifyFocusedRect(
-            TextFieldValue(text = "Hello, World", selection = TextRange(25)),
-            mDelegate,
-            textLayoutResult,
-            layoutCoordinates,
-            textInputSession,
-            true /* hasFocus */,
-            offsetMapping
-        )
-        verify(textInputSession).notifyFocusedRect(any())
-    }
-
-    private class MockCoordinates(
-        override val size: IntSize = IntSize.Zero,
-        val localOffset: Offset = Offset.Zero,
-        val globalOffset: Offset = Offset.Zero,
-        val rootOffset: Offset = Offset.Zero
-    ) : LayoutCoordinates {
-        override val providedAlignmentLines: Set<AlignmentLine>
-            get() = emptySet()
-        override val parentLayoutCoordinates: LayoutCoordinates?
-            get() = null
-        override val parentCoordinates: LayoutCoordinates?
-            get() = null
-        override val isAttached: Boolean
-            get() = true
-
-        override fun windowToLocal(relativeToWindow: Offset): Offset = localOffset
-
-        override fun localToWindow(relativeToLocal: Offset): Offset = globalOffset
-
-        override fun localToRoot(relativeToLocal: Offset): Offset = rootOffset
-        override fun localPositionOf(
-            sourceCoordinates: LayoutCoordinates,
-            relativeToSource: Offset
-        ): Offset = Offset.Zero
-
-        override fun localBoundingBoxOf(
-            sourceCoordinates: LayoutCoordinates,
-            clipBounds: Boolean
-        ): Rect = Rect.Zero
-
-        override fun get(alignmentLine: AlignmentLine): Int = 0
-    }
 }
\ No newline at end of file
diff --git a/compose/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/ui/specification/SpecificationItem.kt b/compose/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/ui/specification/SpecificationItem.kt
index 89dda08..24c5f18 100644
--- a/compose/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/ui/specification/SpecificationItem.kt
+++ b/compose/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/ui/specification/SpecificationItem.kt
@@ -16,39 +16,42 @@
 
 package androidx.compose.material.catalog.ui.specification
 
-import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.catalog.model.Specification
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.KeyboardArrowRight
+import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
+import androidx.compose.material3.OutlinedCard
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun SpecificationItem(
     specification: Specification,
     onClick: (specification: Specification) -> Unit
 ) {
-    // TODO: Replace with M3 Card when available
-    Surface(
-        onClick = { onClick(specification) },
-        modifier = Modifier.fillMaxWidth(),
-        shape = SpecificationItemShape,
-        border = BorderStroke(
-            width = SpecificationItemBorderWidth,
-            color = MaterialTheme.colorScheme.outline
-        )
+    val interactionSource = remember { MutableInteractionSource() }
+    OutlinedCard(
+        modifier = Modifier
+            .fillMaxWidth()
+            .clickable(
+                interactionSource = interactionSource,
+                indication = null,
+                onClick = { onClick(specification) }),
+        interactionSource = interactionSource
     ) {
         Row(
             modifier = Modifier.padding(SpecificationItemPadding),
@@ -79,5 +82,3 @@
 
 private val SpecificationItemPadding = 16.dp
 private val SpecificationItemTextPadding = 8.dp
-private val SpecificationItemBorderWidth = 1.dp
-private val SpecificationItemShape = RoundedCornerShape(12.dp)
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index f96bf21..dfaab717 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -583,18 +583,29 @@
   }
 
   public final class TextFieldDefaults {
+    method public float getFocusedBorderThickness();
     method public float getMinHeight();
     method public float getMinWidth();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getOutlinedTextFieldShape();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getTextFieldShape();
+    method public float getUnfocusedBorderThickness();
     method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors outlinedTextFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
     method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors textFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
+    property public final float FocusedBorderThickness;
     property public final float MinHeight;
     property public final float MinWidth;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape OutlinedTextFieldShape;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape TextFieldShape;
+    property public final float UnfocusedBorderThickness;
     field public static final float BackgroundOpacity = 0.12f;
     field public static final androidx.compose.material.TextFieldDefaults INSTANCE;
     field public static final float IconOpacity = 0.54f;
     field public static final float UnfocusedIndicatorLineOpacity = 0.42f;
   }
 
+  public final class TextFieldDefaultsKt {
+  }
+
   public final class TextFieldImplKt {
   }
 
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index fffe2db..8916a08 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -810,18 +810,36 @@
   }
 
   public final class TextFieldDefaults {
+    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void BorderStroke(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape, optional float focusedBorderThickness, optional float unfocusedBorderThickness);
+    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> border);
+    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
+    method public float getFocusedBorderThickness();
     method public float getMinHeight();
     method public float getMinWidth();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getOutlinedTextFieldShape();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getTextFieldShape();
+    method public float getUnfocusedBorderThickness();
+    method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.ui.Modifier indicatorLine(androidx.compose.ui.Modifier, boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material.TextFieldColors colors, optional float focusedIndicatorLineThickness, optional float unfocusedIndicatorLineThickness);
     method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors outlinedTextFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
+    method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues outlinedTextFieldPadding(optional float start, optional float top, optional float end, optional float bottom);
     method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors textFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
+    method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues textFieldWithLabelPadding(optional float start, optional float end, optional float top, optional float bottom);
+    method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues textFieldWithoutLabelPadding(optional float start, optional float top, optional float end, optional float bottom);
+    property public final float FocusedBorderThickness;
     property public final float MinHeight;
     property public final float MinWidth;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape OutlinedTextFieldShape;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape TextFieldShape;
+    property public final float UnfocusedBorderThickness;
     field public static final float BackgroundOpacity = 0.12f;
     field public static final androidx.compose.material.TextFieldDefaults INSTANCE;
     field public static final float IconOpacity = 0.54f;
     field public static final float UnfocusedIndicatorLineOpacity = 0.42f;
   }
 
+  public final class TextFieldDefaultsKt {
+  }
+
   public final class TextFieldImplKt {
   }
 
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index f96bf21..dfaab717 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -583,18 +583,29 @@
   }
 
   public final class TextFieldDefaults {
+    method public float getFocusedBorderThickness();
     method public float getMinHeight();
     method public float getMinWidth();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getOutlinedTextFieldShape();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getTextFieldShape();
+    method public float getUnfocusedBorderThickness();
     method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors outlinedTextFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
     method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors textFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
+    property public final float FocusedBorderThickness;
     property public final float MinHeight;
     property public final float MinWidth;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape OutlinedTextFieldShape;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape TextFieldShape;
+    property public final float UnfocusedBorderThickness;
     field public static final float BackgroundOpacity = 0.12f;
     field public static final androidx.compose.material.TextFieldDefaults INSTANCE;
     field public static final float IconOpacity = 0.54f;
     field public static final float UnfocusedIndicatorLineOpacity = 0.42f;
   }
 
+  public final class TextFieldDefaultsKt {
+  }
+
   public final class TextFieldImplKt {
   }
 
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt
index 229ca93..d31c42f 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialDemos.kt
@@ -106,6 +106,7 @@
             listOf(
                 ComposableDemo("FilledTextField/OutlinedTextField") { MaterialTextFieldDemo() },
                 ComposableDemo("Multiple text fields") { TextFieldsDemo() },
+                ComposableDemo("Textfield decoration box") { DecorationBoxDemos() },
                 ComposableDemo("Alignment inside text fields") { VerticalAlignmentsInTextField() }
             )
         )
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
index 83fb12e..858e9de 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
@@ -146,7 +146,9 @@
         TextField(
             value = text.value,
             onValueChange = { text.value = it },
-            label = if (label.value) { @Composable { Text("Label") } } else null,
+            label = if (label.value) {
+                @Composable { Text("Label") }
+            } else null,
             singleLine = singleLine.value,
             modifier = textFieldModifier
         )
@@ -154,7 +156,9 @@
         OutlinedTextField(
             value = text.value,
             onValueChange = { text.value = it },
-            label = if (label.value) { @Composable { Text("Label") } } else null,
+            label = if (label.value) {
+                @Composable { Text("Label") }
+            } else null,
             singleLine = singleLine.value,
             modifier = textFieldModifier
         )
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/TextFieldDecorationBoxDemos.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/TextFieldDecorationBoxDemos.kt
new file mode 100644
index 0000000..b6d93db
--- /dev/null
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/TextFieldDecorationBoxDemos.kt
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2022 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.material.demos
+
+import androidx.compose.animation.core.RepeatMode
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.infiniteRepeatable
+import androidx.compose.animation.core.rememberInfiniteTransition
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
+import androidx.compose.foundation.interaction.InteractionSource
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsFocusedAsState
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material.ContentAlpha
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.material.TextFieldColors
+import androidx.compose.material.TextFieldDefaults
+import androidx.compose.material.TextFieldDefaults.OutlinedTextFieldDecorationBox
+import androidx.compose.material.TextFieldDefaults.TextFieldDecorationBox
+import androidx.compose.material.TextFieldDefaults.indicatorLine
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+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.composed
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun DecorationBoxDemos() {
+    LazyColumn(
+        modifier = Modifier.wrapContentSize(Alignment.Center).width(280.dp),
+        verticalArrangement = Arrangement.spacedBy(16.dp),
+        contentPadding = PaddingValues(vertical = 16.dp)
+    ) {
+        item {
+            Text("Progressing indicator")
+            IndicatorLineTextField(IndicatorType.Progress)
+        }
+        item {
+            Text("Animated indicator")
+            IndicatorLineTextField(IndicatorType.Animated)
+        }
+        item {
+            Text("Animated gradient indicator")
+            IndicatorLineTextField(IndicatorType.Gradient)
+        }
+        item {
+            Text("Dense outlined text field")
+            DenseOutlinedTextField()
+        }
+        item {
+            Text("Dense text field with custom horizontal padding")
+            DenseTextField()
+        }
+    }
+}
+
+@Composable
+private fun DenseOutlinedTextField() {
+    // 40.dp height single line Outlined text field
+    var text by remember { mutableStateOf("") }
+    val singleLine = true
+    val interactionSource = remember { MutableInteractionSource() }
+    BasicTextField(
+        value = text,
+        onValueChange = { text = it },
+        modifier = Modifier.height(40.dp).width(TextFieldDefaults.MinWidth),
+        singleLine = singleLine,
+        interactionSource = interactionSource
+    ) { innerTextField ->
+        @OptIn(ExperimentalMaterialApi::class)
+        OutlinedTextFieldDecorationBox(
+            value = text,
+            innerTextField = innerTextField,
+            enabled = true,
+            singleLine = singleLine,
+            visualTransformation = VisualTransformation.None,
+            interactionSource = interactionSource,
+            contentPadding = TextFieldDefaults.outlinedTextFieldPadding(
+                // make it dense, Modifier.height controls the height in this case
+                top = 0.dp, bottom = 0.dp
+            )
+        )
+    }
+}
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+private fun DenseTextField() {
+    var text by remember { mutableStateOf("") }
+    val singleLine = true
+    val enabled = true
+    val interactionSource = remember { MutableInteractionSource() }
+    BasicTextField(
+        value = text,
+        onValueChange = { text = it },
+        modifier = Modifier
+            .indicatorLine(enabled, false, interactionSource, TextFieldDefaults.textFieldColors())
+            .background(
+                TextFieldDefaults.textFieldColors().backgroundColor(enabled).value,
+                TextFieldDefaults.TextFieldShape
+            )
+            .width(TextFieldDefaults.MinWidth),
+        singleLine = singleLine,
+        interactionSource = interactionSource
+    ) { innerTextField ->
+        TextFieldDecorationBox(
+            value = text,
+            innerTextField = innerTextField,
+            enabled = enabled,
+            singleLine = singleLine,
+            visualTransformation = VisualTransformation.None,
+            interactionSource = interactionSource,
+            label = { Text("Label") },
+            contentPadding = TextFieldDefaults.textFieldWithLabelPadding(
+                start = 4.dp,
+                end = 4.dp,
+                bottom = 4.dp // make it dense
+            )
+        )
+    }
+}
+
+@Composable
+private fun IndicatorLineTextField(type: IndicatorType) {
+    var text by remember { mutableStateOf("") }
+    val singleLine = true
+    val enabled = true
+    val interactionSource = remember { MutableInteractionSource() }
+
+    val colors = TextFieldDefaults.textFieldColors()
+    val indicator = when (type) {
+        IndicatorType.Progress -> Modifier.progressIndicatorLine(text, interactionSource, enabled)
+        IndicatorType.Animated -> Modifier.animatedIndicator(
+            interactionSource, enabled, false, colors
+        )
+        IndicatorType.Gradient -> Modifier.animatedGradient(interactionSource, enabled)
+    }
+    BasicTextField(
+        value = text,
+        onValueChange = { text = it },
+        modifier = indicator
+            .background(colors.backgroundColor(enabled).value, TextFieldDefaults.TextFieldShape)
+            .width(TextFieldDefaults.MinWidth),
+        singleLine = singleLine,
+        interactionSource = interactionSource,
+        enabled = enabled
+    ) { innerTextField ->
+        @OptIn(ExperimentalMaterialApi::class)
+        TextFieldDecorationBox(
+            value = text,
+            innerTextField = innerTextField,
+            enabled = enabled,
+            singleLine = singleLine,
+            visualTransformation = VisualTransformation.None,
+            interactionSource = interactionSource,
+            label = { Text("Label") },
+            colors = colors
+        )
+    }
+}
+
+private const val ExpectedInputLength = 8
+private fun Modifier.progressIndicatorLine(
+    text: String,
+    interactionSource: InteractionSource,
+    enabled: Boolean
+): Modifier = composed {
+    val animationDuration = 150
+    val focused by interactionSource.collectIsFocusedAsState()
+
+    // height of line
+    val targetHeight = if (focused) 4.dp else 2.dp
+    val animatedHeight = if (enabled)
+        animateDpAsState(targetHeight, tween(durationMillis = animationDuration))
+    else
+        rememberUpdatedState(1.dp)
+
+    // width of line
+    val target = (text.length.toFloat() / ExpectedInputLength).coerceAtMost(1.0f)
+    val progress = animateFloatAsState(target, tween(durationMillis = animationDuration))
+
+    val unfocusedEmptyColor = MaterialTheme
+        .colors
+        .onSurface
+        .copy(alpha = TextFieldDefaults.UnfocusedIndicatorLineOpacity)
+    val progressColor = MaterialTheme.colors.primary.copy(alpha = ContentAlpha.high)
+
+    drawWithContent {
+        drawContent()
+        val strokeWidth = animatedHeight.value.value * density
+        val y = size.height - strokeWidth / 2
+        val x = size.width * progress.value
+
+        drawLine(
+            SolidColor(unfocusedEmptyColor),
+            Offset(0f, y),
+            Offset(size.width, y),
+            strokeWidth
+        )
+
+        drawLine(
+            SolidColor(progressColor),
+            Offset(0f, y),
+            Offset(x, y),
+            strokeWidth
+        )
+    }
+}
+
+private fun Modifier.animatedIndicator(
+    interactionSource: InteractionSource,
+    enabled: Boolean,
+    isError: Boolean,
+    colors: TextFieldColors
+): Modifier = composed {
+    val animationDuration = 150
+    val focused by interactionSource.collectIsFocusedAsState()
+    // height of line
+    val targetHeight = if (focused) 2.dp else 1.dp
+    val animatedHeight = if (enabled) {
+        animateDpAsState(targetHeight, tween(animationDuration))
+    } else {
+        rememberUpdatedState(1.dp)
+    }
+    // width of line
+    val targetFloat = if (focused) 1f else 0f
+    val progress = if (enabled) {
+        animateFloatAsState(targetFloat, tween(animationDuration))
+    } else {
+        rememberUpdatedState(1f)
+    }
+
+    val color = colors.indicatorColor(enabled, isError, interactionSource)
+    drawWithContent {
+        drawContent()
+        val strokeWidth = animatedHeight.value.value * density
+        val y = size.height - strokeWidth / 2
+        val deltaX = size.width * (progress.value) / 2
+        val offset1 = if (focused) Offset(size.width / 2 - deltaX, y) else Offset(0f, y)
+        val offset2 = if (focused) Offset(size.width / 2 + deltaX, y) else Offset(size.width, y)
+        drawLine(
+            SolidColor(color.value),
+            offset1,
+            offset2,
+            strokeWidth
+        )
+    }
+}
+
+private fun Modifier.animatedGradient(
+    interactionSource: InteractionSource,
+    enabled: Boolean
+): Modifier = composed {
+    val animationDuration = 150
+    val unfocusedColor =
+        MaterialTheme.colors.onSurface.copy(alpha = TextFieldDefaults.UnfocusedIndicatorLineOpacity)
+    val disabledColor = unfocusedColor.copy(alpha = ContentAlpha.disabled)
+    val focused by interactionSource.collectIsFocusedAsState()
+
+    // height of line
+    val targetHeight = if (focused) 2.dp else 1.dp
+    val animatedHeight = if (enabled) {
+        animateDpAsState(targetHeight, tween(animationDuration))
+    } else {
+        rememberUpdatedState(1.dp)
+    }
+
+    val infiniteTransition = rememberInfiniteTransition()
+    val brush = when {
+        !enabled -> SolidColor(disabledColor)
+        !focused -> SolidColor(unfocusedColor)
+        else -> {
+            val progress = infiniteTransition.animateFloat(
+                0.2f,
+                0.8f,
+                infiniteRepeatable(tween(3_000), RepeatMode.Reverse)
+            )
+            Brush.horizontalGradient(
+                0.0f to Color.Blue,
+                progress.value to Color.Cyan,
+                1f to Color.Blue
+            )
+        }
+    }
+
+    drawWithContent {
+        drawContent()
+
+        val strokeWidth = animatedHeight.value.value * density
+        val y = size.height - strokeWidth / 2
+        drawLine(
+            brush,
+            Offset(0f, y),
+            Offset(size.width, y),
+            strokeWidth
+        )
+    }
+}
+
+private enum class IndicatorType {
+    Progress, Animated, Gradient
+}
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
index 940784a..2f2f2b2 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
@@ -17,18 +17,24 @@
 package androidx.compose.material.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material.ContentAlpha
+import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.OutlinedTextField
 import androidx.compose.material.Text
 import androidx.compose.material.TextField
+import androidx.compose.material.TextFieldDefaults
+import androidx.compose.material.TextFieldDefaults.indicatorLine
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.material.icons.filled.Info
@@ -37,10 +43,13 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.platform.LocalSoftwareKeyboardController
 import androidx.compose.ui.semantics.error
@@ -160,9 +169,10 @@
     TextField(
         value = password,
         onValueChange = { password = it },
+        singleLine = true,
         label = { Text("Enter password") },
         visualTransformation =
-            if (passwordHidden) PasswordVisualTransformation() else VisualTransformation.None,
+        if (passwordHidden) PasswordVisualTransformation() else VisualTransformation.None,
         keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
         trailingIcon = {
             IconButton(onClick = { passwordHidden = !passwordHidden }) {
@@ -312,12 +322,133 @@
 
 @Composable
 fun TextArea() {
-    var text by rememberSaveable { mutableStateOf("This is a very long input that extends beyond " +
-        "the height of the text area.") }
+    var text by rememberSaveable {
+        mutableStateOf(
+            "This is a very long input that extends beyond " +
+                "the height of the text area."
+        )
+    }
     TextField(
         value = text,
         onValueChange = { text = it },
         modifier = Modifier.height(100.dp),
         label = { Text("Label") }
     )
-}
\ No newline at end of file
+}
+
+@Sampled
+@Composable
+fun CustomTextFieldBasedOnDecorationBox() {
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    fun CustomTextField(
+        value: String,
+        onValueChange: (String) -> Unit,
+        modifier: Modifier = Modifier
+    ) {
+        val interactionSource = remember { MutableInteractionSource() }
+        // parameters below will be passed to BasicTextField for correct behavior of the text field,
+        // and to the decoration box for proper styling and sizing
+        val enabled = true
+        val singleLine = true
+        val passwordTransformation = PasswordVisualTransformation()
+
+        val colors = TextFieldDefaults.textFieldColors()
+        BasicTextField(
+            value = value,
+            onValueChange = onValueChange,
+            modifier = modifier
+                .background(
+                    color = colors.backgroundColor(enabled).value,
+                    shape = TextFieldDefaults.TextFieldShape
+                )
+                .indicatorLine(
+                    enabled = enabled,
+                    isError = false,
+                    interactionSource = interactionSource,
+                    colors = colors
+                ),
+            visualTransformation = passwordTransformation,
+            // internal implementation of the BasicTextField will dispatch focus events
+            interactionSource = interactionSource,
+            enabled = enabled,
+            singleLine = singleLine
+        ) {
+            TextFieldDefaults.TextFieldDecorationBox(
+                value = value,
+                visualTransformation = passwordTransformation,
+                innerTextField = it,
+                singleLine = singleLine,
+                enabled = enabled,
+                // same interaction source as the one passed to BasicTextField to read focus state
+                // for text field styling
+                interactionSource = interactionSource,
+                // keep vertical paddings but change the horizontal
+                contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
+                    start = 8.dp, end = 8.dp
+                )
+            )
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun CustomOutlinedTextFieldBasedOnDecorationBox() {
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    fun CustomTextField(
+        value: String,
+        onValueChange: (String) -> Unit,
+        modifier: Modifier = Modifier
+    ) {
+        val interactionSource = remember { MutableInteractionSource() }
+        // parameters below will be passed to BasicTextField for correct behavior of the text field,
+        // and to the decoration box for proper styling and sizing
+        val enabled = true
+        val singleLine = true
+
+        val colors = TextFieldDefaults.outlinedTextFieldColors(
+            unfocusedBorderColor = Color.LightGray,
+            focusedBorderColor = Color.DarkGray
+        )
+        BasicTextField(
+            value = value,
+            onValueChange = onValueChange,
+            modifier = modifier,
+            // internal implementation of the BasicTextField will dispatch focus events
+            interactionSource = interactionSource,
+            enabled = enabled,
+            singleLine = singleLine
+        ) {
+            TextFieldDefaults.OutlinedTextFieldDecorationBox(
+                value = value,
+                visualTransformation = VisualTransformation.None,
+                innerTextField = it,
+                singleLine = singleLine,
+                enabled = enabled,
+                // same interaction source as the one passed to BasicTextField to read focus state
+                // for text field styling
+                interactionSource = interactionSource,
+                // keep vertical paddings but change the horizontal
+                contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
+                    start = 8.dp, end = 8.dp
+                ),
+                // update border thickness and shape
+                border = {
+                    TextFieldDefaults.BorderStroke(
+                        enabled = enabled,
+                        isError = false,
+                        colors = colors,
+                        interactionSource = interactionSource,
+                        shape = RectangleShape,
+                        unfocusedBorderThickness = 2.dp,
+                        focusedBorderThickness = 4.dp
+                    )
+                },
+                // update border colors
+                colors = colors
+            )
+        }
+    }
+}
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt
index 049911e..51539f4 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt
@@ -20,7 +20,10 @@
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.interaction.PressInteraction
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.width
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -28,6 +31,10 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.layout.boundsInParent
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.testTag
@@ -36,13 +43,13 @@
 import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
-import androidx.compose.ui.test.assertRangeInfoEquals
 import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertRangeInfoEquals
 import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.performSemanticsAction
+import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -574,7 +581,7 @@
         }
     }
 
-    @ExperimentalMaterialApi
+    @OptIn(ExperimentalMaterialApi::class)
     @Test
     fun rangeSlider_dragThumb() {
         val state = mutableStateOf(0f..1f)
@@ -608,7 +615,7 @@
         }
     }
 
-    @ExperimentalMaterialApi
+    @OptIn(ExperimentalMaterialApi::class)
     @Test
     fun rangeSlider_drag_out_of_bounds() {
         val state = mutableStateOf(0f..1f)
@@ -645,7 +652,7 @@
         }
     }
 
-    @ExperimentalMaterialApi
+    @OptIn(ExperimentalMaterialApi::class)
     @Test
     fun rangeSlider_tap() {
         val state = mutableStateOf(0f..1f)
@@ -676,7 +683,7 @@
         }
     }
 
-    @ExperimentalMaterialApi
+    @OptIn(ExperimentalMaterialApi::class)
     @Test
     fun rangeSlider_tap_rangeChange() {
         val state = mutableStateOf(0f..25f)
@@ -709,7 +716,7 @@
         }
     }
 
-    @ExperimentalMaterialApi
+    @OptIn(ExperimentalMaterialApi::class)
     @Test
     fun rangeSlider_drag_rtl() {
         val state = mutableStateOf(0f..1f)
@@ -746,7 +753,7 @@
         }
     }
 
-    @ExperimentalMaterialApi
+    @OptIn(ExperimentalMaterialApi::class)
     @Test
     fun rangeSlider_drag_out_of_bounds_rtl() {
         val state = mutableStateOf(0f..1f)
@@ -786,7 +793,7 @@
         }
     }
 
-    @ExperimentalMaterialApi
+    @OptIn(ExperimentalMaterialApi::class)
     @Test
     fun rangeSlider_closeThumbs_dragRight() {
         val state = mutableStateOf(0.5f..0.5f)
@@ -822,7 +829,7 @@
         }
     }
 
-    @ExperimentalMaterialApi
+    @OptIn(ExperimentalMaterialApi::class)
     @Test
     fun rangeSlider_closeThumbs_dragLeft() {
         val state = mutableStateOf(0.5f..0.5f)
@@ -857,4 +864,33 @@
             Truth.assertThat(state.value.endInclusive).isEqualTo(0.5f)
         }
     }
+
+    /**
+     * Regression test for bug: 210289161 where RangeSlider was ignoring some modifiers like weight.
+     */
+    @OptIn(ExperimentalMaterialApi::class)
+    @Test
+    fun rangeSlider_weightModifier() {
+        var sliderBounds = Rect(0f, 0f, 0f, 0f)
+        rule.setMaterialContent {
+            with(LocalDensity.current) {
+                Row(Modifier.width(500.toDp())) {
+                    Spacer(Modifier.requiredSize(100.toDp()))
+                    RangeSlider(
+                        values = 0f..0.5f,
+                        onValueChange = {},
+                        modifier = Modifier.testTag(tag).weight(1f).onGloballyPositioned {
+                            sliderBounds = it.boundsInParent()
+                        }
+                    )
+                    Spacer(Modifier.requiredSize(100.toDp()))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(sliderBounds.left).isEqualTo(100)
+            Truth.assertThat(sliderBounds.right).isEqualTo(400)
+        }
+    }
 }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldDecorationBoxTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldDecorationBoxTest.kt
new file mode 100644
index 0000000..6fb3cd5
--- /dev/null
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldDecorationBoxTest.kt
@@ -0,0 +1,650 @@
+/*
+ * Copyright 2022 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.material.textfield
+
+import android.os.Build
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.FirstBaselineOffset
+import androidx.compose.material.Text
+import androidx.compose.material.TextFieldBottomPadding
+import androidx.compose.material.TextFieldDefaults
+import androidx.compose.material.TextFieldDefaults.OutlinedTextFieldDecorationBox
+import androidx.compose.material.TextFieldDefaults.TextFieldDecorationBox
+import androidx.compose.material.TextFieldDefaults.indicatorLine
+import androidx.compose.material.TextFieldPadding
+import androidx.compose.material.TextFieldTopPadding
+import androidx.compose.material.setMaterialContent
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertPixels
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalMaterialApi::class)
+class TextFieldDecorationBoxTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val Density = Density(1f)
+    private val InnerTextFieldHeight = 50.dp
+    private val InnerTextFieldWidth = 100.dp
+
+    @Test
+    fun outlinedTextFieldBox_overrideTopPadding_multiLine() {
+        assertVerticalSizeAndPosition_outlinedTextField(
+            TextFieldDefaults.outlinedTextFieldPadding(top = 10.dp),
+            false,
+            10.dp + InnerTextFieldHeight + TextFieldPadding,
+            10.dp
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideTopPadding_singleLine() {
+        assertVerticalSizeAndPosition_outlinedTextField(
+            TextFieldDefaults.outlinedTextFieldPadding(top = 10.dp),
+            true,
+            10.dp + InnerTextFieldHeight + TextFieldPadding,
+            (10.dp + TextFieldPadding) / 2
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideBottomPadding_multiLine() {
+        assertVerticalSizeAndPosition_outlinedTextField(
+            TextFieldDefaults.outlinedTextFieldPadding(bottom = 10.dp),
+            false,
+            TextFieldPadding + InnerTextFieldHeight + 10.dp,
+            TextFieldPadding
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideBottomPadding_singleLine() {
+        assertVerticalSizeAndPosition_outlinedTextField(
+            TextFieldDefaults.outlinedTextFieldPadding(bottom = 10.dp),
+            true,
+            TextFieldPadding + InnerTextFieldHeight + 10.dp,
+            (10.dp + TextFieldPadding) / 2
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideStartPadding() {
+        assertHorizontalSizeAndPosition_outlinedTextField(
+            TextFieldDefaults.outlinedTextFieldPadding(start = 10.dp),
+            false,
+            10.dp + InnerTextFieldWidth + TextFieldPadding,
+            10.dp
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideStartPadding_rtl() {
+        assertHorizontalSizeAndPosition_outlinedTextField(
+            TextFieldDefaults.outlinedTextFieldPadding(start = 10.dp),
+            true,
+            10.dp + InnerTextFieldWidth + TextFieldPadding,
+            TextFieldPadding
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideEndPadding() {
+        assertHorizontalSizeAndPosition_outlinedTextField(
+            TextFieldDefaults.outlinedTextFieldPadding(end = 20.dp),
+            false,
+            TextFieldPadding + InnerTextFieldWidth + 20.dp,
+            TextFieldPadding
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideEndPadding_rtl() {
+        assertHorizontalSizeAndPosition_outlinedTextField(
+            TextFieldDefaults.outlinedTextFieldPadding(end = 20.dp),
+            true,
+            TextFieldPadding + InnerTextFieldWidth + 20.dp,
+            20.dp
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideTopPadding_singleLine_withoutLabel() {
+        assertVerticalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithoutLabelPadding(top = 40.dp),
+            singleLine = true,
+            hasLabel = false,
+            40.dp + InnerTextFieldHeight + TextFieldPadding,
+            (40.dp + TextFieldPadding) / 2
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideTopPadding_singleLine_withLabel() {
+        assertVerticalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithLabelPadding(top = 40.dp),
+            singleLine = true,
+            hasLabel = true,
+            40.dp + TextFieldTopPadding + InnerTextFieldHeight + TextFieldBottomPadding,
+            40.dp + TextFieldTopPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideBottomPadding_singleLine_withoutLabel() {
+        assertVerticalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithoutLabelPadding(bottom = 40.dp),
+            singleLine = true,
+            hasLabel = false,
+            TextFieldPadding + InnerTextFieldHeight + 40.dp,
+            (TextFieldPadding + 40.dp) / 2
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideBottomPadding_singleLine_withLabel() {
+        assertVerticalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithLabelPadding(bottom = 40.dp),
+            singleLine = true,
+            hasLabel = true,
+            FirstBaselineOffset + TextFieldTopPadding + InnerTextFieldHeight + 40.dp,
+            FirstBaselineOffset + TextFieldTopPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideTopPadding_multiLine_withoutLabel() {
+        assertVerticalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithoutLabelPadding(top = 40.dp),
+            singleLine = false,
+            hasLabel = false,
+            40.dp + InnerTextFieldHeight + TextFieldPadding,
+            40.dp
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideTopPadding_multiLine_withLabel() {
+        assertVerticalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithLabelPadding(top = 40.dp),
+            singleLine = false,
+            hasLabel = true,
+            40.dp + TextFieldTopPadding + InnerTextFieldHeight + TextFieldBottomPadding,
+            40.dp + TextFieldTopPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideBottomPadding_multiLine_withoutLabel() {
+        assertVerticalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithoutLabelPadding(bottom = 40.dp),
+            singleLine = false,
+            hasLabel = false,
+            TextFieldPadding + InnerTextFieldHeight + 40.dp,
+            TextFieldPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideBottomPadding_multiLine_withLabel() {
+        assertVerticalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithLabelPadding(bottom = 40.dp),
+            singleLine = false,
+            hasLabel = true,
+            FirstBaselineOffset + TextFieldTopPadding + InnerTextFieldHeight + 40.dp,
+            FirstBaselineOffset + TextFieldTopPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideStartPadding_withLabel() {
+        assertHorizontalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithLabelPadding(start = 40.dp),
+            false,
+            hasLabel = true,
+            40.dp + InnerTextFieldWidth + TextFieldPadding,
+            40.dp
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideStartPadding_withLabel_rtl() {
+        assertHorizontalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithLabelPadding(start = 40.dp),
+            true,
+            hasLabel = true,
+            40.dp + InnerTextFieldWidth + TextFieldPadding,
+            TextFieldPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideStartPadding_withoutLabel() {
+        assertHorizontalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithoutLabelPadding(start = 40.dp),
+            false,
+            hasLabel = false,
+            40.dp + InnerTextFieldWidth + TextFieldPadding,
+            40.dp
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideStartPadding_withoutLabel_rtl() {
+        assertHorizontalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithoutLabelPadding(start = 40.dp),
+            true,
+            hasLabel = false,
+            40.dp + InnerTextFieldWidth + TextFieldPadding,
+            TextFieldPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideEndPadding_withLabel() {
+        assertHorizontalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithLabelPadding(end = 40.dp),
+            false,
+            hasLabel = true,
+            TextFieldPadding + InnerTextFieldWidth + 40.dp,
+            TextFieldPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideEndPadding_withLabel_rtl() {
+        assertHorizontalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithLabelPadding(end = 40.dp),
+            true,
+            hasLabel = true,
+            TextFieldPadding + InnerTextFieldWidth + 40.dp,
+            40.dp
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideEndPadding_withoutLabel() {
+        assertHorizontalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithoutLabelPadding(end = 40.dp),
+            false,
+            hasLabel = false,
+            TextFieldPadding + InnerTextFieldWidth + 40.dp,
+            TextFieldPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideEndPadding_withoutLabel_rtl() {
+        assertHorizontalSizeAndPosition_textField(
+            TextFieldDefaults.textFieldWithoutLabelPadding(end = 40.dp),
+            true,
+            hasLabel = false,
+            TextFieldPadding + InnerTextFieldWidth + 40.dp,
+            40.dp
+        )
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun outlinedTextFieldBox_defaultBorderColor_comesFromColors() {
+        val textFieldWidth = 300
+        val textFieldHeight = 150
+        val borderWidth = 40
+        val value = "Text"
+
+        rule.setMaterialContent {
+            CompositionLocalProvider(LocalDensity provides Density) {
+                val interactionSource = remember { MutableInteractionSource() }
+                val singleLine = true
+                val colors = TextFieldDefaults.outlinedTextFieldColors(
+                    unfocusedBorderColor = Color.Red
+                )
+                BasicTextField(
+                    value = value,
+                    onValueChange = {},
+                    modifier = Modifier.size(
+                        with(Density) { textFieldWidth.toDp() },
+                        with(Density) { textFieldHeight.toDp() }
+                    ),
+                    singleLine = singleLine,
+                    interactionSource = interactionSource
+                ) {
+                    OutlinedTextFieldDecorationBox(
+                        value = value,
+                        innerTextField = it,
+                        enabled = true,
+                        visualTransformation = VisualTransformation.None,
+                        interactionSource = interactionSource,
+                        singleLine = singleLine,
+                        border = {
+                            TextFieldDefaults.BorderStroke(
+                                enabled = true,
+                                isError = false,
+                                colors = colors,
+                                interactionSource = interactionSource,
+                                shape = RectangleShape,
+                                unfocusedBorderThickness = with(Density) { borderWidth.toDp() }
+                            )
+                        },
+                        colors = colors,
+                        contentPadding = PaddingValues(0.dp)
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithText(value)
+            .captureToImage()
+            .assertPixels(IntSize(textFieldWidth, textFieldHeight)) {
+                // to account for edge pixels
+                if (it.x in 2..(textFieldWidth - 2) && it.y in 2..(borderWidth - 2)) {
+                    Color.Red
+                } else null
+            }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun textFieldBox_defaultIndicatorLineColor_comesFromColors() {
+        val textFieldWidth = 300
+        val textFieldHeight = 150
+        val borderWidth = 40
+        val value = "Text"
+
+        rule.setMaterialContent {
+            CompositionLocalProvider(LocalDensity provides Density) {
+                val interactionSource = remember { MutableInteractionSource() }
+                val singleLine = true
+                val colors = TextFieldDefaults.textFieldColors(
+                    unfocusedIndicatorColor = Color.Red
+                )
+                BasicTextField(
+                    value = value,
+                    onValueChange = {},
+                    modifier = Modifier
+                        .indicatorLine(
+                            enabled = true,
+                            isError = false,
+                            colors = colors,
+                            interactionSource = interactionSource,
+                            unfocusedIndicatorLineThickness = with(Density) { borderWidth.toDp() }
+                        )
+                        .size(
+                            with(Density) { textFieldWidth.toDp() },
+                            with(Density) { textFieldHeight.toDp() }
+                        ),
+                    singleLine = singleLine,
+                    interactionSource = interactionSource
+                ) {
+                    TextFieldDecorationBox(
+                        value = value,
+                        innerTextField = it,
+                        enabled = true,
+                        visualTransformation = VisualTransformation.None,
+                        interactionSource = interactionSource,
+                        singleLine = singleLine,
+                        colors = colors,
+                        contentPadding = PaddingValues(0.dp)
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithText(value)
+            .captureToImage()
+            .assertPixels(IntSize(textFieldWidth, textFieldHeight)) {
+                // to account for edge pixels
+                if (it.x in 2..(textFieldWidth - 2) &&
+                    it.y in (textFieldHeight - borderWidth + 2)..(textFieldHeight - 2)
+                ) {
+                    Color.Red
+                } else null
+            }
+    }
+
+    private fun assertVerticalSizeAndPosition_outlinedTextField(
+        padding: PaddingValues,
+        singleLine: Boolean,
+        expectedHeight: Dp,
+        expectedPosition: Dp
+    ) {
+        assertSizeAndPosition_outlinedTextField(
+            padding,
+            singleLine,
+            expectedHeight,
+            expectedPosition,
+            true
+        )
+    }
+
+    private fun assertHorizontalSizeAndPosition_outlinedTextField(
+        padding: PaddingValues,
+        rtl: Boolean,
+        expectedHeight: Dp,
+        expectedPosition: Dp
+    ) {
+        assertSizeAndPosition_outlinedTextField(
+            padding,
+            true,
+            expectedHeight,
+            expectedPosition,
+            false,
+            if (rtl) LayoutDirection.Rtl else LayoutDirection.Ltr
+        )
+    }
+
+    private fun assertSizeAndPosition_outlinedTextField(
+        padding: PaddingValues,
+        singleLine: Boolean,
+        expectedSize: Dp,
+        expectedPosition: Dp,
+        vertical: Boolean,
+        layoutDirection: LayoutDirection = LayoutDirection.Ltr
+    ) {
+        var size: IntSize? = null
+        var position: Offset? = null
+        rule.setMaterialContent {
+            CompositionLocalProvider(
+                LocalLayoutDirection provides layoutDirection,
+                LocalDensity provides Density
+            ) {
+                Box(Modifier.onSizeChanged { size = it }) {
+                    val value = ""
+                    val interactionSource = remember { MutableInteractionSource() }
+                    BasicTextField(
+                        value = value,
+                        onValueChange = {},
+                        singleLine = singleLine,
+                        interactionSource = interactionSource
+                    ) {
+                        OutlinedTextFieldDecorationBox(
+                            value = value,
+                            innerTextField = {
+                                Box(
+                                    Modifier
+                                        .size(InnerTextFieldWidth, InnerTextFieldHeight)
+                                        .onGloballyPositioned {
+                                            position = it.positionInRoot()
+                                        }
+                                ) { it() }
+                            },
+                            enabled = true,
+                            singleLine = singleLine,
+                            visualTransformation = VisualTransformation.None,
+                            interactionSource = interactionSource,
+                            contentPadding = padding
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            with(Density) {
+                assertThat(size).isNotNull()
+                if (vertical) {
+                    assertThat(size!!.height).isEqualTo(expectedSize.roundToPx())
+                } else {
+                    assertThat(size!!.width).isEqualTo(expectedSize.roundToPx())
+                }
+                assertThat(position).isNotNull()
+                if (vertical) {
+                    assertThat(position!!.y.roundToInt()).isEqualTo(expectedPosition.roundToPx())
+                } else {
+                    assertThat(position!!.x.roundToInt()).isEqualTo(expectedPosition.roundToPx())
+                }
+            }
+        }
+    }
+
+    private fun assertVerticalSizeAndPosition_textField(
+        padding: PaddingValues,
+        singleLine: Boolean,
+        hasLabel: Boolean,
+        expectedHeight: Dp,
+        expectedPosition: Dp
+    ) {
+        assertSizeAndPosition_textField(
+            padding,
+            singleLine,
+            expectedHeight,
+            expectedPosition,
+            true,
+            hasLabel
+        )
+    }
+
+    private fun assertHorizontalSizeAndPosition_textField(
+        padding: PaddingValues,
+        rtl: Boolean,
+        hasLabel: Boolean,
+        expectedHeight: Dp,
+        expectedPosition: Dp
+    ) {
+        assertSizeAndPosition_textField(
+            padding,
+            true,
+            expectedHeight,
+            expectedPosition,
+            false,
+            hasLabel,
+            if (rtl) LayoutDirection.Rtl else LayoutDirection.Ltr
+        )
+    }
+
+    private fun assertSizeAndPosition_textField(
+        padding: PaddingValues,
+        singleLine: Boolean,
+        expectedSize: Dp,
+        expectedPosition: Dp,
+        vertical: Boolean,
+        hasLabel: Boolean,
+        layoutDirection: LayoutDirection = LayoutDirection.Ltr
+    ) {
+        var size: IntSize? = null
+        var position: Offset? = null
+        rule.setMaterialContent {
+            CompositionLocalProvider(
+                LocalLayoutDirection provides layoutDirection,
+                LocalDensity provides Density
+            ) {
+                Box(Modifier.onSizeChanged { size = it }) {
+                    val value = "Text"
+                    val interactionSource = remember { MutableInteractionSource() }
+                    BasicTextField(
+                        value = value,
+                        onValueChange = {},
+                        singleLine = singleLine,
+                        interactionSource = interactionSource
+                    ) {
+                        val label: @Composable (() -> Unit)? = if (hasLabel) {
+                            @Composable { Text("Label") }
+                        } else null
+                        TextFieldDecorationBox(
+                            value = value,
+                            innerTextField = {
+                                Box(
+                                    Modifier
+                                        .size(InnerTextFieldWidth, InnerTextFieldHeight)
+                                        .onGloballyPositioned {
+                                            position = it.positionInRoot()
+                                        }
+                                ) { it() }
+                            },
+                            enabled = true,
+                            singleLine = singleLine,
+                            visualTransformation = VisualTransformation.None,
+                            interactionSource = interactionSource,
+                            contentPadding = padding,
+                            label = label
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            with(Density) {
+                assertThat(size).isNotNull()
+                if (vertical) {
+                    assertThat(size!!.height).isEqualTo(expectedSize.roundToPx())
+                } else {
+                    assertThat(size!!.width).isEqualTo(expectedSize.roundToPx())
+                }
+                assertThat(position).isNotNull()
+                if (vertical) {
+                    assertThat(position!!.y.roundToInt()).isEqualTo(expectedPosition.roundToPx())
+                } else {
+                    assertThat(position!!.x.roundToInt()).isEqualTo(expectedPosition.roundToPx())
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
index f9112ff..6c4c2c9 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
@@ -417,7 +417,7 @@
                 ExpectedPadding.roundToPx().toFloat()
             )
             assertThat(labelPosition.value?.y).isEqualTo(
-                TextFieldPadding.roundToPx()
+                ExpectedPadding.roundToPx()
             )
         }
     }
@@ -451,12 +451,11 @@
             assertThat(labelSize.value).isNotNull()
             assertThat(labelSize.value?.height).isGreaterThan(0)
             assertThat(labelSize.value?.width).isGreaterThan(0)
-            // centered position
             assertThat(labelPosition.value?.x).isEqualTo(
                 ExpectedPadding.roundToPx().toFloat()
             )
             assertThat(labelPosition.value?.y).isEqualTo(
-                TextFieldPadding.roundToPx()
+                ExpectedPadding.roundToPx()
             )
         }
     }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
index 6bbb9bc..52e709e 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
@@ -17,17 +17,17 @@
 package androidx.compose.material
 
 import androidx.compose.foundation.background
-import androidx.compose.foundation.border
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.calculateEndPadding
+import androidx.compose.foundation.layout.calculateStartPadding
 import androidx.compose.foundation.layout.defaultMinSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material.TextFieldDefaults.MinHeight
-import androidx.compose.material.TextFieldDefaults.MinWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -38,10 +38,10 @@
 import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.ClipOp
-import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.drawscope.clipRect
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.layout.IntrinsicMeasurable
 import androidx.compose.ui.layout.IntrinsicMeasureScope
 import androidx.compose.ui.layout.Layout
@@ -51,15 +51,16 @@
 import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.coerceAtLeast
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.offset
 import kotlin.math.max
@@ -105,8 +106,9 @@
  * @param isError indicates if the text field's current value is in error. If set to true, the
  * label, bottom indicator and trailing icon by default will be displayed in error color
  * @param visualTransformation transforms the visual representation of the input [value]
- * For example, you can use [androidx.compose.ui.text.input.PasswordVisualTransformation] to create a password
- * text field. By default no visual transformation is applied
+ * For example, you can use
+ * [PasswordVisualTransformation][androidx.compose.ui.text.input.PasswordVisualTransformation] to
+ * create a password text field. By default no visual transformation is applied
  * @param keyboardOptions software keyboard options that contains configuration such as
  * [KeyboardType] and [ImeAction]
  * @param keyboardActions when the input service emits an IME action, the corresponding callback
@@ -222,8 +224,9 @@
  * @param isError indicates if the text field's current value is in error state. If set to
  * true, the label, bottom indicator and trailing icon by default will be displayed in error color
  * @param visualTransformation transforms the visual representation of the input [value]
- * For example, you can use [androidx.compose.ui.text.input.PasswordVisualTransformation] to create a password
- * text field. By default no visual transformation is applied
+ * For example, you can use
+ * [PasswordVisualTransformation][androidx.compose.ui.text.input.PasswordVisualTransformation] to
+ * create a password text field. By default no visual transformation is applied
  * @param keyboardOptions software keyboard options that contains configuration such as
  * [KeyboardType] and [ImeAction]
  * @param keyboardActions when the input service emits an IME action, the corresponding callback
@@ -264,112 +267,62 @@
     singleLine: Boolean = false,
     maxLines: Int = Int.MAX_VALUE,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    shape: Shape = MaterialTheme.shapes.small,
+    shape: Shape = TextFieldDefaults.OutlinedTextFieldShape,
     colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
 ) {
-    TextFieldImpl(
-        type = TextFieldType.Outlined,
-        enabled = enabled,
-        readOnly = readOnly,
-        value = value,
-        onValueChange = onValueChange,
-        modifier = modifier,
-        singleLine = singleLine,
-        textStyle = textStyle,
-        label = label,
-        placeholder = placeholder,
-        leading = leadingIcon,
-        trailing = trailingIcon,
-        isError = isError,
-        visualTransformation = visualTransformation,
-        keyboardOptions = keyboardOptions,
-        keyboardActions = keyboardActions,
-        maxLines = maxLines,
-        interactionSource = interactionSource,
-        shape = shape,
-        colors = colors
-    )
-}
+    // If color is not provided via the text style, use content color as a default
+    val textColor = textStyle.color.takeOrElse {
+        colors.textColor(enabled).value
+    }
+    val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
 
-@Composable
-internal fun OutlinedTextFieldLayout(
-    modifier: Modifier,
-    value: TextFieldValue,
-    onValueChange: (TextFieldValue) -> Unit,
-    enabled: Boolean,
-    readOnly: Boolean,
-    keyboardOptions: KeyboardOptions,
-    keyboardActions: KeyboardActions,
-    textStyle: TextStyle,
-    singleLine: Boolean,
-    maxLines: Int = Int.MAX_VALUE,
-    visualTransformation: VisualTransformation,
-    interactionSource: MutableInteractionSource,
-    decoratedPlaceholder: @Composable ((Modifier) -> Unit)?,
-    decoratedLabel: @Composable (() -> Unit)?,
-    leading: @Composable (() -> Unit)?,
-    trailing: @Composable (() -> Unit)?,
-    leadingColor: Color,
-    trailingColor: Color,
-    labelProgress: Float,
-    indicatorWidth: Dp,
-    indicatorColor: Color,
-    cursorColor: Color,
-    backgroundColor: Color,
-    shape: Shape
-) {
-    val labelSize = remember { mutableStateOf(Size.Zero) }
+    @OptIn(ExperimentalMaterialApi::class)
     BasicTextField(
         value = value,
-        modifier = modifier
-            .then(
-                if (decoratedLabel != null) {
-                    Modifier.padding(top = OutlinedTextFieldTopPadding)
-                } else {
-                    Modifier
-                }
-            )
+        modifier = if (label != null) {
+            modifier.padding(top = OutlinedTextFieldTopPadding)
+        } else {
+            modifier
+        }
+            .background(colors.backgroundColor(enabled).value, shape)
             .defaultMinSize(
-                minWidth = MinWidth,
-                minHeight = MinHeight
-            )
-            .background(backgroundColor, shape),
+                minWidth = TextFieldDefaults.MinWidth,
+                minHeight = TextFieldDefaults.MinHeight
+            ),
         onValueChange = onValueChange,
         enabled = enabled,
         readOnly = readOnly,
-        textStyle = textStyle,
-        cursorBrush = SolidColor(cursorColor),
+        textStyle = mergedTextStyle,
+        cursorBrush = SolidColor(colors.cursorColor(isError).value),
         visualTransformation = visualTransformation,
         keyboardOptions = keyboardOptions,
         keyboardActions = keyboardActions,
         interactionSource = interactionSource,
         singleLine = singleLine,
         maxLines = maxLines,
-        decorationBox = @Composable { coreTextField ->
-            // places leading icon, input field, label, placeholder, trailing icon
-            IconsWithTextFieldLayout(
-                textField = coreTextField,
-                leading = leading,
-                trailing = trailing,
+        decorationBox = @Composable { innerTextField ->
+            TextFieldDefaults.OutlinedTextFieldDecorationBox(
+                value = value.text,
+                visualTransformation = visualTransformation,
+                innerTextField = innerTextField,
+                placeholder = placeholder,
+                label = label,
+                leadingIcon = leadingIcon,
+                trailingIcon = trailingIcon,
                 singleLine = singleLine,
-                leadingColor = leadingColor,
-                trailingColor = trailingColor,
-                onLabelMeasured = {
-                    val labelWidth = it.width * labelProgress
-                    val labelHeight = it.height * labelProgress
-                    if (labelSize.value.width != labelWidth ||
-                        labelSize.value.height != labelHeight
-                    ) {
-                        labelSize.value = Size(labelWidth, labelHeight)
-                    }
-                },
-                animationProgress = labelProgress,
-                placeholder = decoratedPlaceholder,
-                label = decoratedLabel,
-                shape = shape,
-                borderWidth = indicatorWidth,
-                borderColor = indicatorColor,
-                labelSize = labelSize.value
+                enabled = enabled,
+                isError = isError,
+                interactionSource = interactionSource,
+                colors = colors,
+                border = {
+                    TextFieldDefaults.BorderStroke(
+                        enabled,
+                        isError,
+                        interactionSource,
+                        colors,
+                        shape
+                    )
+                }
             )
         }
     )
@@ -382,47 +335,44 @@
  * positioned in the middle part.
 \ */
 @Composable
-private fun IconsWithTextFieldLayout(
+internal fun OutlinedTextFieldLayout(
+    modifier: Modifier,
     textField: @Composable () -> Unit,
     placeholder: @Composable ((Modifier) -> Unit)?,
     label: @Composable (() -> Unit)?,
     leading: @Composable (() -> Unit)?,
     trailing: @Composable (() -> Unit)?,
     singleLine: Boolean,
-    leadingColor: Color,
-    trailingColor: Color,
     animationProgress: Float,
     onLabelMeasured: (Size) -> Unit,
-    shape: Shape,
-    borderWidth: Dp,
-    borderColor: Color,
-    labelSize: Size
+    border: @Composable () -> Unit,
+    paddingValues: PaddingValues
 ) {
-    val measurePolicy = remember(onLabelMeasured, singleLine, animationProgress) {
-        OutlinedTextFieldMeasurePolicy(onLabelMeasured, singleLine, animationProgress)
+    val measurePolicy = remember(onLabelMeasured, singleLine, animationProgress, paddingValues) {
+        OutlinedTextFieldMeasurePolicy(
+            onLabelMeasured,
+            singleLine,
+            animationProgress,
+            paddingValues
+        )
     }
+    val layoutDirection = LocalLayoutDirection.current
     Layout(
+        modifier = modifier,
         content = {
             // We use additional box here to place an outlined cutout border as a sibling after the
             // rest of UI. This allows us to use Modifier.border to draw an outline on top of the
             // text field. We can't use the border modifier directly on the IconsWithTextFieldLayout
             // as we also need to do the clipping (to form the cutout) which should not affect
             // the rest of text field UI
-            Box(
-                Modifier
-                    .layoutId("border")
-                    .outlinedBorder(shape, borderWidth, borderColor, labelSize)
-            )
+            border()
 
             if (leading != null) {
                 Box(
                     modifier = Modifier.layoutId(LeadingId).then(IconDefaultSizeModifier),
                     contentAlignment = Alignment.Center
                 ) {
-                    Decoration(
-                        contentColor = leadingColor,
-                        content = leading
-                    )
+                    leading()
                 }
             }
             if (trailing != null) {
@@ -430,16 +380,25 @@
                     modifier = Modifier.layoutId(TrailingId).then(IconDefaultSizeModifier),
                     contentAlignment = Alignment.Center
                 ) {
-                    Decoration(
-                        contentColor = trailingColor,
-                        content = trailing
-                    )
+                    trailing()
                 }
             }
-            val paddingToIcon = TextFieldPadding - HorizontalIconPadding
+
+            val startTextFieldPadding = paddingValues.calculateStartPadding(layoutDirection)
+            val endTextFieldPadding = paddingValues.calculateEndPadding(layoutDirection)
             val padding = Modifier.padding(
-                start = if (leading != null) paddingToIcon else TextFieldPadding,
-                end = if (trailing != null) paddingToIcon else TextFieldPadding
+                start = if (leading != null) {
+                    (startTextFieldPadding - HorizontalIconPadding).coerceAtLeast(
+                        0.dp
+                    )
+                } else {
+                    startTextFieldPadding
+                },
+                end = if (trailing != null) {
+                    (endTextFieldPadding - HorizontalIconPadding).coerceAtLeast(0.dp)
+                } else {
+                    endTextFieldPadding
+                }
             )
             if (placeholder != null) {
                 placeholder(Modifier.layoutId(PlaceholderId).then(padding))
@@ -461,9 +420,10 @@
 }
 
 private class OutlinedTextFieldMeasurePolicy(
-    val onLabelMeasured: (Size) -> Unit,
-    val singleLine: Boolean,
-    val animationProgress: Float
+    private val onLabelMeasured: (Size) -> Unit,
+    private val singleLine: Boolean,
+    private val animationProgress: Float,
+    private val paddingValues: PaddingValues
 ) : MeasurePolicy {
     override fun MeasureScope.measure(
         measurables: List<Measurable>,
@@ -471,7 +431,7 @@
     ): MeasureResult {
         // used to calculate the constraints for measuring elements that will be placed in a row
         var occupiedSpaceHorizontally = 0
-        val bottomPadding = TextFieldPadding.roundToPx()
+        val bottomPadding = paddingValues.calculateBottomPadding().roundToPx()
 
         // measure leading icon
         val relaxedConstraints = constraints.copy(minWidth = 0, minHeight = 0)
@@ -502,7 +462,7 @@
 
         // measure text field
         // on top we offset either by default padding or by label's half height if its too big
-        // minWidth must not be set to 0 due to how foundation TextField treats zero minWidth
+        // minHeight must not be set to 0 due to how foundation TextField treats zero minHeight
         val topPadding = max(heightOrZero(labelPlaceable) / 2, bottomPadding)
         val textConstraints = constraints.offset(
             horizontal = -occupiedSpaceHorizontally,
@@ -533,10 +493,11 @@
                 heightOrZero(labelPlaceable),
                 heightOrZero(placeholderPlaceable),
                 constraints,
-                density
+                density,
+                paddingValues
             )
 
-        val borderPlaceable = measurables.first { it.layoutId == "border" }.measure(
+        val borderPlaceable = measurables.first { it.layoutId == BorderId }.measure(
             Constraints(
                 minWidth = if (width != Constraints.Infinity) width else 0,
                 maxWidth = width,
@@ -556,7 +517,9 @@
                 borderPlaceable,
                 animationProgress,
                 singleLine,
-                density
+                density,
+                layoutDirection,
+                paddingValues
             )
         }
     }
@@ -652,7 +615,8 @@
             labelPlaceableHeight = labelHeight,
             placeholderPlaceableHeight = placeholderHeight,
             constraints = ZeroConstraints,
-            density = density
+            density = density,
+            paddingValues = paddingValues
         )
     }
 }
@@ -690,7 +654,8 @@
     labelPlaceableHeight: Int,
     placeholderPlaceableHeight: Int,
     constraints: Constraints,
-    density: Float
+    density: Float,
+    paddingValues: PaddingValues
 ): Int {
     // middle section is defined as a height of the text field or placeholder ( whichever is
     // taller) plus 16.dp or half height of the label if it is taller, given that the label
@@ -699,9 +664,10 @@
         textFieldPlaceableHeight,
         placeholderPlaceableHeight
     )
-    val topBottomPadding = TextFieldPadding.value * density
-    val middleSectionHeight = inputFieldHeight + topBottomPadding + max(
-        topBottomPadding,
+    val topPadding = paddingValues.calculateTopPadding().value * density
+    val bottomPadding = paddingValues.calculateBottomPadding().value * density
+    val middleSectionHeight = inputFieldHeight + bottomPadding + max(
+        topPadding,
         labelPlaceableHeight / 2f
     )
     return max(
@@ -729,9 +695,14 @@
     borderPlaceable: Placeable,
     animationProgress: Float,
     singleLine: Boolean,
-    density: Float
+    density: Float,
+    layoutDirection: LayoutDirection,
+    paddingValues: PaddingValues
 ) {
-    val topBottomPadding = (TextFieldPadding.value * density).roundToInt()
+    val topPadding = (paddingValues.calculateTopPadding().value * density).roundToInt()
+    val startPadding =
+        (paddingValues.calculateStartPadding(layoutDirection).value * density).roundToInt()
+
     val iconPadding = HorizontalIconPadding.value * density
 
     // placed center vertically and to the start edge horizontally
@@ -752,7 +723,7 @@
         val startPositionY = if (singleLine) {
             Alignment.CenterVertically.align(it.height, height)
         } else {
-            topBottomPadding
+            topPadding
         }
         val positionY =
             startPositionY * (1 - animationProgress) - (it.height / 2) * animationProgress
@@ -762,7 +733,7 @@
             } else {
                 (widthOrZero(leadingPlaceable) - iconPadding) * (1 - animationProgress)
             }
-            ).roundToInt() + topBottomPadding
+            ).roundToInt() + startPadding
         it.placeRelative(positionX, positionY.roundToInt())
     }
 
@@ -771,7 +742,7 @@
     val textVerticalPosition = if (singleLine) {
         Alignment.CenterVertically.align(textFieldPlaceable.height, height)
     } else {
-        topBottomPadding
+        topPadding
     }
     textFieldPlaceable.placeRelative(widthOrZero(leadingPlaceable), textVerticalPosition)
 
@@ -780,7 +751,7 @@
         val placeholderVerticalPosition = if (singleLine) {
             Alignment.CenterVertically.align(it.height, height)
         } else {
-            topBottomPadding
+            topPadding
         }
         it.placeRelative(widthOrZero(leadingPlaceable), placeholderVerticalPosition)
     }
@@ -789,36 +760,20 @@
     borderPlaceable.place(IntOffset.Zero)
 }
 
-/**
- * Draws an outlined border with label cutout
- */
-private fun Modifier.outlinedBorder(
-    shape: Shape,
-    borderWidth: Dp,
-    borderColor: Color,
-    labelSize: Size
-) = this
-    .outlineCutout(labelSize)
-    .border(
-        width = borderWidth,
-        color = borderColor,
-        shape = shape
-    )
-
-private fun Modifier.outlineCutout(labelSize: Size) =
+internal fun Modifier.outlineCutout(labelSize: Size, paddingValues: PaddingValues) =
     this.drawWithContent {
         val labelWidth = labelSize.width
         if (labelWidth > 0f) {
             val innerPadding = OutlinedTextFieldInnerPadding.toPx()
-            val leftLtr = TextFieldPadding.toPx() - innerPadding
+            val leftLtr = paddingValues.calculateLeftPadding(layoutDirection).toPx() - innerPadding
             val rightLtr = leftLtr + labelWidth + 2 * innerPadding
             val left = when (layoutDirection) {
-                LayoutDirection.Ltr -> leftLtr
                 LayoutDirection.Rtl -> size.width - rightLtr
+                else -> leftLtr.coerceAtLeast(0f)
             }
             val right = when (layoutDirection) {
-                LayoutDirection.Ltr -> rightLtr
-                LayoutDirection.Rtl -> size.width - leftLtr
+                LayoutDirection.Rtl -> size.width - leftLtr.coerceAtLeast(0f)
+                else -> rightLtr
             }
             val labelHeight = labelSize.height
             // using label height as a cutout area to make sure that no hairline artifacts are
@@ -839,3 +794,5 @@
 need to add additional padding themselves
 */
 private val OutlinedTextFieldTopPadding = 8.dp
+
+internal const val BorderId = "border"
\ No newline at end of file
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
index 558bd2b..ea5b393 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
@@ -286,7 +286,8 @@
     }
 
     BoxWithConstraints(
-        modifier = Modifier
+        modifier = modifier
+            .minimumTouchTargetSize()
             .requiredSizeIn(minWidth = ThumbRadius * 4, minHeight = ThumbRadius * 2)
     ) {
         val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
@@ -348,7 +349,7 @@
             }
         }
 
-        val pressDrag = modifier.rangeSliderPressDragModifier(
+        val pressDrag = Modifier.rangeSliderPressDragModifier(
             startInteractionSource,
             endInteractionSource,
             rawOffsetStart,
@@ -389,7 +390,7 @@
             maxPx - minPx,
             startInteractionSource,
             endInteractionSource,
-            modifier = pressDrag.then(modifier),
+            modifier = pressDrag,
         )
     }
 }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
index c8c45d1..a7a8c7b 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
@@ -48,10 +48,6 @@
 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.util.fastForEachIndexed
-import androidx.compose.ui.util.fastMap
-import androidx.compose.ui.util.fastMaxBy
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 
@@ -153,29 +149,29 @@
             val tabMeasurables = subcompose(TabSlots.Tabs, tabs)
             val tabCount = tabMeasurables.size
             val tabWidth = (tabRowWidth / tabCount)
-            val tabPlaceables = tabMeasurables.fastMap {
+            val tabPlaceables = tabMeasurables.map {
                 it.measure(constraints.copy(minWidth = tabWidth, maxWidth = tabWidth))
             }
 
-            val tabRowHeight = tabPlaceables.fastMaxBy { it.height }?.height ?: 0
+            val tabRowHeight = tabPlaceables.maxByOrNull { it.height }?.height ?: 0
 
             val tabPositions = List(tabCount) { index ->
                 TabPosition(tabWidth.toDp() * index, tabWidth.toDp())
             }
 
             layout(tabRowWidth, tabRowHeight) {
-                tabPlaceables.fastForEachIndexed { index, placeable ->
+                tabPlaceables.forEachIndexed { index, placeable ->
                     placeable.placeRelative(index * tabWidth, 0)
                 }
 
-                subcompose(TabSlots.Divider, divider).fastForEach {
+                subcompose(TabSlots.Divider, divider).forEach {
                     val placeable = it.measure(constraints.copy(minHeight = 0))
                     placeable.placeRelative(0, tabRowHeight - placeable.height)
                 }
 
                 subcompose(TabSlots.Indicator) {
                     indicator(tabPositions)
-                }.fastForEach {
+                }.forEach {
                     it.measure(Constraints.fixed(tabRowWidth, tabRowHeight)).placeRelative(0, 0)
                 }
             }
@@ -260,11 +256,11 @@
             val tabConstraints = constraints.copy(minWidth = minTabWidth)
 
             val tabPlaceables = subcompose(TabSlots.Tabs, tabs)
-                .fastMap { it.measure(tabConstraints) }
+                .map { it.measure(tabConstraints) }
 
             var layoutWidth = padding * 2
             var layoutHeight = 0
-            tabPlaceables.fastForEach {
+            tabPlaceables.forEach {
                 layoutWidth += it.width
                 layoutHeight = maxOf(layoutHeight, it.height)
             }
@@ -274,7 +270,7 @@
                 // Place the tabs
                 val tabPositions = mutableListOf<TabPosition>()
                 var left = padding
-                tabPlaceables.fastForEach {
+                tabPlaceables.forEach {
                     it.placeRelative(left, 0)
                     tabPositions.add(TabPosition(left = left.toDp(), width = it.width.toDp()))
                     left += it.width
@@ -282,7 +278,7 @@
 
                 // The divider is measured with its own height, and width equal to the total width
                 // of the tab row, and then placed on top of the tabs.
-                subcompose(TabSlots.Divider, divider).fastForEach {
+                subcompose(TabSlots.Divider, divider).forEach {
                     val placeable = it.measure(
                         constraints.copy(
                             minHeight = 0,
@@ -297,7 +293,7 @@
                 // row, and then placed on top of the divider.
                 subcompose(TabSlots.Indicator) {
                     indicator(tabPositions)
-                }.fastForEach {
+                }.forEach {
                     it.measure(Constraints.fixed(layoutWidth, layoutHeight)).placeRelative(0, 0)
                 }
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
index e9de4b5..e7f31b6 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
@@ -16,18 +16,21 @@
 
 package androidx.compose.material
 
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.background
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.calculateEndPadding
+import androidx.compose.foundation.layout.calculateStartPadding
 import androidx.compose.foundation.layout.defaultMinSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.shape.ZeroCornerSize
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material.TextFieldDefaults.MinHeight
-import androidx.compose.material.TextFieldDefaults.MinWidth
+import androidx.compose.material.TextFieldDefaults.indicatorLine
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -35,11 +38,11 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.layout.AlignmentLine
 import androidx.compose.ui.layout.IntrinsicMeasurable
 import androidx.compose.ui.layout.IntrinsicMeasureScope
@@ -51,6 +54,7 @@
 import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
@@ -58,6 +62,7 @@
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.coerceAtLeast
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.offset
 import kotlin.math.max
@@ -128,8 +133,9 @@
  * @param isError indicates if the text field's current value is in error. If set to true, the
  * label, bottom indicator and trailing icon by default will be displayed in error color
  * @param visualTransformation transforms the visual representation of the input [value]
- * For example, you can use [androidx.compose.ui.text.input.PasswordVisualTransformation] to create a password
- * text field. By default no visual transformation is applied
+ * For example, you can use
+ * [PasswordVisualTransformation][androidx.compose.ui.text.input.PasswordVisualTransformation] to
+ * create a password text field. By default no visual transformation is applied
  * @param keyboardOptions software keyboard options that contains configuration such as
  * [KeyboardType] and [ImeAction].
  * @param keyboardActions when the input service emits an IME action, the corresponding callback
@@ -246,8 +252,9 @@
  * @param isError indicates if the text field's current value is in error state. If set to
  * true, the label, bottom indicator and trailing icon by default will be displayed in error color
  * @param visualTransformation transforms the visual representation of the input [value].
- * For example, you can use [androidx.compose.ui.text.input.PasswordVisualTransformation] to create a password
- * text field. By default no visual transformation is applied
+ * For example, you can use
+ * [PasswordVisualTransformation][androidx.compose.ui.text.input.PasswordVisualTransformation] to
+ * create a password text field. By default no visual transformation is applied
  * @param keyboardOptions software keyboard options that contains configuration such as
  * [KeyboardType] and [ImeAction].
  * @param keyboardActions when the input service emits an IME action, the corresponding callback
@@ -288,129 +295,85 @@
     singleLine: Boolean = false,
     maxLines: Int = Int.MAX_VALUE,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    shape: Shape =
-        MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
+    shape: Shape = TextFieldDefaults.TextFieldShape,
     colors: TextFieldColors = TextFieldDefaults.textFieldColors()
 ) {
-    TextFieldImpl(
-        type = TextFieldType.Filled,
-        enabled = enabled,
-        readOnly = readOnly,
-        value = value,
-        onValueChange = onValueChange,
-        modifier = modifier,
-        singleLine = singleLine,
-        textStyle = textStyle,
-        label = label,
-        placeholder = placeholder,
-        leading = leadingIcon,
-        trailing = trailingIcon,
-        isError = isError,
-        visualTransformation = visualTransformation,
-        keyboardOptions = keyboardOptions,
-        keyboardActions = keyboardActions,
-        maxLines = maxLines,
-        interactionSource = interactionSource,
-        shape = shape,
-        colors = colors
-    )
-}
+    // If color is not provided via the text style, use content color as a default
+    val textColor = textStyle.color.takeOrElse {
+        colors.textColor(enabled).value
+    }
+    val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
 
-@Composable
-internal fun TextFieldLayout(
-    modifier: Modifier,
-    value: TextFieldValue,
-    onValueChange: (TextFieldValue) -> Unit,
-    enabled: Boolean,
-    readOnly: Boolean,
-    keyboardOptions: KeyboardOptions,
-    keyboardActions: KeyboardActions,
-    textStyle: TextStyle,
-    singleLine: Boolean,
-    maxLines: Int = Int.MAX_VALUE,
-    visualTransformation: VisualTransformation,
-    interactionSource: MutableInteractionSource,
-    decoratedPlaceholder: @Composable ((Modifier) -> Unit)?,
-    decoratedLabel: @Composable (() -> Unit)?,
-    leading: @Composable (() -> Unit)?,
-    trailing: @Composable (() -> Unit)?,
-    leadingColor: Color,
-    trailingColor: Color,
-    labelProgress: Float,
-    indicatorWidth: Dp,
-    indicatorColor: Color,
-    backgroundColor: Color,
-    cursorColor: Color,
-    shape: Shape
-) {
-
+    @OptIn(ExperimentalMaterialApi::class)
     BasicTextField(
         value = value,
         modifier = modifier
+            .background(colors.backgroundColor(enabled).value, shape)
+            .indicatorLine(enabled, isError, interactionSource, colors)
             .defaultMinSize(
-                minWidth = MinWidth,
-                minHeight = MinHeight
-            )
-            .background(color = backgroundColor, shape = shape)
-            .drawIndicatorLine(lineWidth = indicatorWidth, color = indicatorColor),
+                minWidth = TextFieldDefaults.MinWidth,
+                minHeight = TextFieldDefaults.MinHeight
+            ),
         onValueChange = onValueChange,
         enabled = enabled,
         readOnly = readOnly,
-        textStyle = textStyle,
-        cursorBrush = SolidColor(cursorColor),
+        textStyle = mergedTextStyle,
+        cursorBrush = SolidColor(colors.cursorColor(isError).value),
         visualTransformation = visualTransformation,
         keyboardOptions = keyboardOptions,
         keyboardActions = keyboardActions,
         interactionSource = interactionSource,
         singleLine = singleLine,
         maxLines = maxLines,
-        decorationBox = @Composable { coreTextField ->
+        decorationBox = @Composable { innerTextField ->
             // places leading icon, text field with label and placeholder, trailing icon
-            IconsWithTextFieldLayout(
-                textField = coreTextField,
-                placeholder = decoratedPlaceholder,
-                label = decoratedLabel,
-                leading = leading,
-                trailing = trailing,
+            TextFieldDefaults.TextFieldDecorationBox(
+                value = value.text,
+                visualTransformation = visualTransformation,
+                innerTextField = innerTextField,
+                placeholder = placeholder,
+                label = label,
+                leadingIcon = leadingIcon,
+                trailingIcon = trailingIcon,
                 singleLine = singleLine,
-                leadingColor = leadingColor,
-                trailingColor = trailingColor,
-                animationProgress = labelProgress
+                enabled = enabled,
+                isError = isError,
+                interactionSource = interactionSource,
+                colors = colors
             )
         }
     )
 }
 
 /**
- * Layout of the leading and trailing icons and the input field, label and placeholder in
- * [TextField].
+ * Composable responsible for measuring and laying out leading and trailing icons, label,
+ * placeholder and the input field.
  */
 @Composable
-private fun IconsWithTextFieldLayout(
+internal fun TextFieldLayout(
+    modifier: Modifier,
     textField: @Composable () -> Unit,
     label: @Composable (() -> Unit)?,
     placeholder: @Composable ((Modifier) -> Unit)?,
     leading: @Composable (() -> Unit)?,
     trailing: @Composable (() -> Unit)?,
     singleLine: Boolean,
-    leadingColor: Color,
-    trailingColor: Color,
-    animationProgress: Float
+    animationProgress: Float,
+    paddingValues: PaddingValues
 ) {
-    val measurePolicy = remember(singleLine, animationProgress) {
-        TextFieldMeasurePolicy(singleLine, animationProgress)
+    val measurePolicy = remember(singleLine, animationProgress, paddingValues) {
+        TextFieldMeasurePolicy(singleLine, animationProgress, paddingValues)
     }
+    val layoutDirection = LocalLayoutDirection.current
     Layout(
+        modifier = modifier,
         content = {
             if (leading != null) {
                 Box(
                     modifier = Modifier.layoutId(LeadingId).then(IconDefaultSizeModifier),
                     contentAlignment = Alignment.Center
                 ) {
-                    Decoration(
-                        contentColor = leadingColor,
-                        content = leading
-                    )
+                    leading()
                 }
             }
             if (trailing != null) {
@@ -418,17 +381,25 @@
                     modifier = Modifier.layoutId(TrailingId).then(IconDefaultSizeModifier),
                     contentAlignment = Alignment.Center
                 ) {
-                    Decoration(
-                        contentColor = trailingColor,
-                        content = trailing
-                    )
+                    trailing()
                 }
             }
 
-            val paddingToIcon = TextFieldPadding - HorizontalIconPadding
+            val startTextFieldPadding = paddingValues.calculateStartPadding(layoutDirection)
+            val endTextFieldPadding = paddingValues.calculateEndPadding(layoutDirection)
             val padding = Modifier.padding(
-                start = if (leading != null) paddingToIcon else TextFieldPadding,
-                end = if (trailing != null) paddingToIcon else TextFieldPadding
+                start = if (leading != null) {
+                    (startTextFieldPadding - HorizontalIconPadding).coerceAtLeast(
+                        0.dp
+                    )
+                } else {
+                    startTextFieldPadding
+                },
+                end = if (trailing != null) {
+                    (endTextFieldPadding - HorizontalIconPadding).coerceAtLeast(0.dp)
+                } else {
+                    endTextFieldPadding
+                }
             )
             if (placeholder != null) {
                 placeholder(Modifier.layoutId(PlaceholderId).then(padding))
@@ -449,15 +420,17 @@
 
 private class TextFieldMeasurePolicy(
     private val singleLine: Boolean,
-    private val animationProgress: Float
+    private val animationProgress: Float,
+    private val paddingValues: PaddingValues
 ) : MeasurePolicy {
     override fun MeasureScope.measure(
         measurables: List<Measurable>,
         constraints: Constraints
     ): MeasureResult {
-        val topBottomPadding = TextFieldPadding.roundToPx()
-        val baseLineOffset = FirstBaselineOffset.roundToPx()
-        val bottomPadding = LastBaselineOffset.roundToPx()
+        val topPaddingValue = paddingValues.calculateTopPadding().roundToPx()
+        val bottomPaddingValue = paddingValues.calculateBottomPadding().roundToPx()
+
+        // padding between label and input text
         val topPadding = TextFieldTopPadding.roundToPx()
         var occupiedSpaceHorizontally = 0
 
@@ -479,7 +452,7 @@
         // measure label
         val labelConstraints = looseConstraints
             .offset(
-                vertical = -bottomPadding,
+                vertical = -bottomPaddingValue,
                 horizontal = -occupiedSpaceHorizontally
             )
         val labelPlaceable =
@@ -487,14 +460,14 @@
         val lastBaseline = labelPlaceable?.get(LastBaseline)?.let {
             if (it != AlignmentLine.Unspecified) it else labelPlaceable.height
         } ?: 0
-        val effectiveLabelBaseline = max(lastBaseline, baseLineOffset)
+        val effectiveLabelBaseline = max(lastBaseline, topPaddingValue)
 
         // measure input field
         // input field is laid out differently depending on whether the label is present or not
         val verticalConstraintOffset = if (labelPlaceable != null) {
-            -bottomPadding - topPadding - effectiveLabelBaseline
+            -bottomPaddingValue - topPadding - effectiveLabelBaseline
         } else {
-            -topBottomPadding * 2
+            -topPaddingValue - bottomPaddingValue
         }
         val textFieldConstraints = constraints
             .copy(minHeight = 0)
@@ -528,13 +501,14 @@
             heightOrZero(trailingPlaceable),
             heightOrZero(placeholderPlaceable),
             constraints,
-            density
+            density,
+            paddingValues
         )
 
         return layout(width, height) {
             if (labelPlaceable != null) {
                 // label's final position is always relative to the baseline
-                val labelEndPosition = (baseLineOffset - lastBaseline).coerceAtLeast(0)
+                val labelEndPosition = (topPaddingValue - lastBaseline).coerceAtLeast(0)
                 placeWithLabel(
                     width,
                     height,
@@ -558,7 +532,8 @@
                     leadingPlaceable,
                     trailingPlaceable,
                     singleLine,
-                    density
+                    density,
+                    paddingValues
                 )
             }
         }
@@ -656,7 +631,8 @@
             trailingHeight = trailingHeight,
             placeholderHeight = placeholderHeight,
             constraints = ZeroConstraints,
-            density = density
+            density = density,
+            paddingValues = paddingValues
         )
     }
 }
@@ -686,17 +662,18 @@
     trailingHeight: Int,
     placeholderHeight: Int,
     constraints: Constraints,
-    density: Float
+    density: Float,
+    paddingValues: PaddingValues
 ): Int {
-    val bottomPadding = LastBaselineOffset.value * density
-    val topPadding = TextFieldTopPadding.value * density
-    val topBottomPadding = TextFieldPadding.value * density
+    val paddingToLabel = TextFieldTopPadding.value * density
+    val topPaddingValue = paddingValues.calculateTopPadding().value * density
+    val bottomPaddingValue = paddingValues.calculateBottomPadding().value * density
 
     val inputFieldHeight = max(textFieldHeight, placeholderHeight)
     val middleSectionHeight = if (hasLabel) {
-        labelBaseline + topPadding + inputFieldHeight + bottomPadding
+        labelBaseline + paddingToLabel + inputFieldHeight + bottomPaddingValue
     } else {
-        topBottomPadding * 2 + inputFieldHeight
+        topPaddingValue + inputFieldHeight + bottomPaddingValue
     }
     return maxOf(
         middleSectionHeight.roundToInt(),
@@ -723,8 +700,6 @@
     animationProgress: Float,
     density: Float
 ) {
-    val topBottomPadding = (TextFieldPadding.value * density).roundToInt()
-
     leadingPlaceable?.placeRelative(
         0,
         Alignment.CenterVertically.align(leadingPlaceable.height, height)
@@ -740,7 +715,9 @@
         val startPosition = if (singleLine) {
             Alignment.CenterVertically.align(it.height, height)
         } else {
-            topBottomPadding
+            // even though the padding is defined by developer, it only affects text field when animation progress == 1,
+            // which is when text field is focused or non-empty input text. The start position of the label is always 16.dp.
+            (TextFieldPadding.value * density).roundToInt()
         }
         val distance = startPosition - labelEndPosition
         val positionY = startPosition - (distance * animationProgress).roundToInt()
@@ -762,9 +739,10 @@
     leadingPlaceable: Placeable?,
     trailingPlaceable: Placeable?,
     singleLine: Boolean,
-    density: Float
+    density: Float,
+    paddingValues: PaddingValues
 ) {
-    val topBottomPadding = (TextFieldPadding.value * density).roundToInt()
+    val topPadding = (paddingValues.calculateTopPadding().value * density).roundToInt()
 
     leadingPlaceable?.placeRelative(
         0,
@@ -780,7 +758,7 @@
     val textVerticalPosition = if (singleLine) {
         Alignment.CenterVertically.align(textPlaceable.height, height)
     } else {
-        topBottomPadding
+        topPadding
     }
     textPlaceable.placeRelative(
         widthOrZero(leadingPlaceable),
@@ -792,7 +770,7 @@
         val placeholderVerticalPosition = if (singleLine) {
             Alignment.CenterVertically.align(placeholderPlaceable.height, height)
         } else {
-            topBottomPadding
+            topPadding
         }
         it.placeRelative(
             widthOrZero(leadingPlaceable),
@@ -804,12 +782,15 @@
 /**
  * A draw modifier that draws a bottom indicator line in [TextField]
  */
-internal fun Modifier.drawIndicatorLine(lineWidth: Dp, color: Color): Modifier {
-    return drawBehind {
-        val strokeWidth = lineWidth.value * density
+internal fun Modifier.drawIndicatorLine(indicatorBorder: BorderStroke): Modifier {
+    val strokeWidthDp = indicatorBorder.width
+    return drawWithContent {
+        drawContent()
+        if (strokeWidthDp == Dp.Hairline) return@drawWithContent
+        val strokeWidth = strokeWidthDp.value * density
         val y = size.height - strokeWidth / 2
         drawLine(
-            color,
+            indicatorBorder.brush,
             Offset(0f, y),
             Offset(size.width, y),
             strokeWidth
@@ -817,6 +798,12 @@
     }
 }
 
-private val FirstBaselineOffset = 20.dp
-private val LastBaselineOffset = 10.dp
-private val TextFieldTopPadding = 4.dp
\ No newline at end of file
+/** Padding from the label's baseline to the top */
+internal val FirstBaselineOffset = 20.dp
+
+/** Padding from input field to the bottom */
+internal val TextFieldBottomPadding = 10.dp
+
+/** Padding from label's baseline (or FirstBaselineOffset) to the input field */
+/*@VisibleForTesting*/
+internal val TextFieldTopPadding = 4.dp
\ No newline at end of file
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt
index 2a56c7b..c8002a1 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt
@@ -17,16 +17,35 @@
 package androidx.compose.material
 
 import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.core.tween
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.border
+import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.InteractionSource
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.interaction.collectIsFocusedAsState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.shape.ZeroCornerSize
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material.TextFieldDefaults.OutlinedTextFieldDecorationBox
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.layout.LastBaseline
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 
 /**
@@ -187,6 +206,35 @@
     const val IconOpacity = 0.54f
 
     /**
+     * The default shape used for a [TextField]'s background
+     */
+    val TextFieldShape: Shape
+        @Composable
+        @ReadOnlyComposable
+        get() = MaterialTheme.shapes.small
+            .copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize)
+
+    /**
+     * The default shape used for a [OutlinedTextField]'s background and border
+     */
+    val OutlinedTextFieldShape: Shape
+        @Composable
+        @ReadOnlyComposable
+        get() = MaterialTheme.shapes.small
+
+    /**
+     * The default thickness of the border in [OutlinedTextField] or indicator line in [TextField]
+     * in unfocused state.
+     */
+    val UnfocusedBorderThickness = 1.dp
+
+    /**
+     * The default thickness of the border in [OutlinedTextField] or indicator line in [TextField]
+     * in focused state.
+     */
+    val FocusedBorderThickness = 2.dp
+
+    /**
      * The default opacity used for a [TextField]'s background color.
      */
     const val BackgroundOpacity = 0.12f
@@ -199,6 +247,127 @@
     const val UnfocusedIndicatorLineOpacity = 0.42f
 
     /**
+     * A modifier to draw a default bottom indicator line in [TextField]. You can use this modifier
+     * if you build your custom text field using [TextFieldDecorationBox] whilst the [TextField]
+     * applies it automatically.
+     *
+     * @param enabled whether the text field is enabled
+     * @param isError whether the text field's current value is in error
+     * @param interactionSource the [InteractionSource] of this text field. Helps to determine if
+     * the text field is in focus or not
+     * @param colors [TextFieldColors] used to resolve colors of the text field
+     * @param focusedIndicatorLineThickness thickness of the indicator line when text field is focused
+     * @param unfocusedIndicatorLineThickness thickness of the indicator line when text field is
+     * not focused
+     */
+    @ExperimentalMaterialApi
+    fun Modifier.indicatorLine(
+        enabled: Boolean,
+        isError: Boolean,
+        interactionSource: InteractionSource,
+        colors: TextFieldColors,
+        focusedIndicatorLineThickness: Dp = FocusedBorderThickness,
+        unfocusedIndicatorLineThickness: Dp = UnfocusedBorderThickness
+    ) = composed(inspectorInfo = debugInspectorInfo {
+        name = "indicatorLine"
+        properties["enabled"] = enabled
+        properties["isError"] = isError
+        properties["interactionSource"] = interactionSource
+        properties["colors"] = colors
+        properties["focusedIndicatorLineThickness"] = focusedIndicatorLineThickness
+        properties["unfocusedIndicatorLineThickness"] = unfocusedIndicatorLineThickness
+    }) {
+        val stroke = animateBorderStrokeAsState(
+            enabled,
+            isError,
+            interactionSource,
+            colors,
+            focusedIndicatorLineThickness,
+            unfocusedIndicatorLineThickness
+        )
+        Modifier.drawIndicatorLine(stroke.value)
+    }
+
+    /**
+     * Composable that draws a default border stroke in [OutlinedTextField]. You can use it to
+     * draw a border stroke in your custom text field based on [OutlinedTextFieldDecorationBox].
+     * The [OutlinedTextField] applies it automatically.
+     *
+     * @param enabled whether the text field is enabled
+     * @param isError whether the text field's current value is in error
+     * @param interactionSource the [InteractionSource] of this text field. Helps to determine if
+     * the text field is in focus or not
+     * @param colors [TextFieldColors] used to resolve colors of the text field
+     * @param focusedBorderThickness thickness of the [OutlinedTextField]'s border when it is in
+     * focused state
+     * @param unfocusedBorderThickness thickness of the [OutlinedTextField]'s border when it is not
+     * in focused state
+     */
+    @ExperimentalMaterialApi
+    @Composable
+    fun BorderStroke(
+        enabled: Boolean,
+        isError: Boolean,
+        interactionSource: InteractionSource,
+        colors: TextFieldColors,
+        shape: Shape = OutlinedTextFieldShape,
+        focusedBorderThickness: Dp = FocusedBorderThickness,
+        unfocusedBorderThickness: Dp = UnfocusedBorderThickness
+    ) {
+        val borderStroke = animateBorderStrokeAsState(
+            enabled,
+            isError,
+            interactionSource,
+            colors,
+            focusedBorderThickness,
+            unfocusedBorderThickness
+        )
+        Box(Modifier.border(borderStroke.value, shape))
+    }
+
+    /**
+     * Default content padding applied to [TextField] when there is a label.
+     *
+     * Note that when label is present, the "top" padding (unlike rest of the paddings) is a
+     * distance between the label's last baseline and the top edge of the [TextField]. If the "top"
+     * value is smaller than the last baseline of the label, then there will be no space between
+     * the label and top edge of the [TextField].
+     *
+     * See [PaddingValues]
+     */
+    @ExperimentalMaterialApi
+    fun textFieldWithLabelPadding(
+        start: Dp = TextFieldPadding,
+        end: Dp = TextFieldPadding,
+        top: Dp = FirstBaselineOffset,
+        bottom: Dp = TextFieldBottomPadding
+    ): PaddingValues = PaddingValues(start, top, end, bottom)
+
+    /**
+     * Default content padding applied to [TextField] when the label is null.
+     * See [PaddingValues] for more details.
+     */
+    @ExperimentalMaterialApi
+    fun textFieldWithoutLabelPadding(
+        start: Dp = TextFieldPadding,
+        top: Dp = TextFieldPadding,
+        end: Dp = TextFieldPadding,
+        bottom: Dp = TextFieldPadding
+    ): PaddingValues = PaddingValues(start, top, end, bottom)
+
+    /**
+     * Default content padding applied to [OutlinedTextField].
+     * See [PaddingValues] for more details.
+     */
+    @ExperimentalMaterialApi
+    fun outlinedTextFieldPadding(
+        start: Dp = TextFieldPadding,
+        top: Dp = TextFieldPadding,
+        end: Dp = TextFieldPadding,
+        bottom: Dp = TextFieldPadding
+    ): PaddingValues = PaddingValues(start, top, end, bottom)
+
+    /**
      * Creates a [TextFieldColors] that represents the default input text, background and content
      * (including label, placeholder, leading and trailing icons) colors used in a [TextField].
      */
@@ -312,6 +481,191 @@
             placeholderColor = placeholderColor,
             disabledPlaceholderColor = disabledPlaceholderColor
         )
+
+    /**
+     * A decoration box which helps creating custom text fields based on
+     * <a href="https://material.io/components/text-fields#filled-text-field" class="external" target="_blank">Material Design filled text field</a>.
+     *
+     * If your text field requires customising elements that aren't exposed by [TextField],
+     * consider using this decoration box to achieve the desired design.
+     *
+     * For example, if you need to create a dense text field, use [contentPadding] parameter to
+     * decrease the paddings around the input field. If you need to customise the bottom indicator,
+     * apply [indicatorLine] modifier to achieve that.
+     *
+     * See example of using [TextFieldDecorationBox] to build your own custom text field
+     * @sample androidx.compose.material.samples.CustomTextFieldBasedOnDecorationBox
+     *C
+     * @param value the input [String] shown by the text field
+     * @param innerTextField input text field that this decoration box wraps. You will pass here a
+     * framework-controlled composable parameter "innerTextField" from the decorationBox lambda of
+     * the [BasicTextField]
+     * @param enabled controls the enabled state of the [TextField]. When `false`, visually
+     * text field will appear in the disabled UI state. You must also pass the same value to the
+     * [BasicTextField] for it to adjust the behavior accordingly making the text field non-editable,
+     * non-focusable and non-selectable
+     * @param singleLine indicates if this is a single line or multi line text field. You must pass
+     * the same value as to [BasicTextField]
+     * @param visualTransformation transforms the visual representation of the input [value]. You must
+     * pass the same value as to [BasicTextField]
+     * @param interactionSource this is a read-only [InteractionSource] representing the stream of
+     * [Interaction]s for this text field. You first create and pass in your own remembered
+     * [MutableInteractionSource] to the [BasicTextField] for it to dispatch events. And then pass the
+     * same instance to this decoration box for it to observe [Interaction]s and customize the
+     * appearance / behavior in different [Interaction]s
+     * @param isError indicates if the text field's current value is in error state. If set to
+     * true, the label, bottom indicator and trailing icon by default will be displayed in error color
+     * @param label the optional label to be displayed inside the text field container. The default
+     * text style for internal [Text] is [Typography.caption] when the text field is in focus and
+     * [Typography.subtitle1] when the text field is not in focus
+     * @param placeholder the optional placeholder to be displayed when the text field is in focus and
+     * the input text is empty. The default text style for internal [Text] is [Typography.subtitle1]
+     * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
+     * container
+     * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+     * container
+     * @param colors [TextFieldColors] that will be used to resolve color of the text and content
+     * (including label, placeholder, leading and trailing icons, bottom indicator) for this text field in
+     * different states. See [TextFieldDefaults.textFieldColors]
+     * @param contentPadding the spacing values to apply internally between the internals of text field
+     * and the decoration box container. You can use it to implement dense text fields or simply to
+     * control horizontal padding. See [TextFieldDefaults.textFieldWithLabelPadding] and
+     * [TextFieldDefaults.textFieldWithoutLabelPadding]
+     * Note that if there's a label in the text field, the [top][PaddingValues.calculateTopPadding]
+     * padding will mean the distance from label's [last baseline][LastBaseline] to the top edge of the
+     * container. All other paddings mean the distance from the corresponding edge of the container to
+     * the corresponding edge of the closest to it element
+     */
+    @Composable
+    @ExperimentalMaterialApi
+    fun TextFieldDecorationBox(
+        value: String,
+        innerTextField: @Composable () -> Unit,
+        enabled: Boolean,
+        singleLine: Boolean,
+        visualTransformation: VisualTransformation,
+        interactionSource: InteractionSource,
+        isError: Boolean = false,
+        label: @Composable (() -> Unit)? = null,
+        placeholder: @Composable (() -> Unit)? = null,
+        leadingIcon: @Composable (() -> Unit)? = null,
+        trailingIcon: @Composable (() -> Unit)? = null,
+        colors: TextFieldColors = textFieldColors(),
+        contentPadding: PaddingValues =
+            if (label == null) {
+                textFieldWithoutLabelPadding()
+            } else {
+                textFieldWithLabelPadding()
+            }
+    ) {
+        CommonDecorationBox(
+            type = TextFieldType.Filled,
+            value = value,
+            innerTextField = innerTextField,
+            visualTransformation = visualTransformation,
+            placeholder = placeholder,
+            label = label,
+            leadingIcon = leadingIcon,
+            trailingIcon = trailingIcon,
+            singleLine = singleLine,
+            enabled = enabled,
+            isError = isError,
+            interactionSource = interactionSource,
+            colors = colors,
+            contentPadding = contentPadding
+        )
+    }
+
+    /**
+     * A decoration box which helps creating custom text fields based on
+     * <a href="https://material.io/components/text-fields#outlined-text-field" class="external" target="_blank">Material Design outlined text field</a>.
+     *
+     * If your text field requires customising elements that aren't exposed by [OutlinedTextField],
+     * consider using this decoration box to achieve the desired design.
+     *
+     * For example, if you need to create a dense outlined text field, use [contentPadding] parameter to
+     * decrease the paddings around the input field. If you need to change the thickness of the border,
+     * use [border] parameter to achieve that.
+     *
+     * Example of custom text field based on [OutlinedTextFieldDecorationBox]:
+     * @sample androidx.compose.material.samples.CustomOutlinedTextFieldBasedOnDecorationBox
+     *
+     * @param value the input [String] shown by the text field
+     * @param innerTextField input text field that this decoration box wraps. You will pass here a
+     * framework-controlled composable parameter "innerTextField" from the decorationBox lambda of the
+     * [BasicTextField]
+     * @param enabled controls the enabled state of the [OutlinedTextField]. When `false`, visually
+     * text field will appear in the disabled UI state. You must also pass the same value to the
+     * [BasicTextField] for it to adjust the behavior accordingly making the text field non-editable,
+     * non-focusable and non-selectable
+     * @param singleLine indicates if this is a single line or multi line text field. You must pass
+     * the same value as to [BasicTextField]
+     * @param visualTransformation transforms the visual representation of the input [value]. You must
+     * pass the same value as to [BasicTextField]
+     * @param interactionSource this is a read-only [InteractionSource] representing the stream of
+     * [Interaction]s for this text field. You first create and pass in your own remembered
+     * [MutableInteractionSource] to the [BasicTextField] for it to dispatch events. And then pass the
+     * same instance to this decoration box for it to observe [Interaction]s and customize the
+     * appearance / behavior in different [Interaction]s.
+     * @param isError indicates if the text field's current value is in error state. If set to
+     * true, the label, bottom indicator and trailing icon by default will be displayed in error color
+     * @param label the optional label to be displayed inside the text field container. The default
+     * text style for internal [Text] is [Typography.caption] when the text field is in focus and
+     * [Typography.subtitle1] when the text field is not in focus
+     * @param placeholder the optional placeholder to be displayed when the text field is in focus and
+     * the input text is empty. The default text style for internal [Text] is [Typography.subtitle1]
+     * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
+     * container
+     * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+     * container
+     * @param colors [TextFieldColors] that will be used to resolve color of the text and content
+     * (including label, placeholder, leading and trailing icons, border) for this text field in
+     * different states. See [TextFieldDefaults.outlinedTextFieldColors]
+     * @param border the border to be drawn around the text field. The cutout to fit the [label] will
+     * be automatically added by the framework. Note that by default the color of the border comes from
+     * the [colors].
+     * @param contentPadding the spacing values to apply internally between the internals of text field
+     * and the decoration box container. You can use it to implement dense text fields or simply to
+     * control horizontal padding. See [TextFieldDefaults.outlinedTextFieldPadding]
+     */
+    @Composable
+    @ExperimentalMaterialApi
+    fun OutlinedTextFieldDecorationBox(
+        value: String,
+        innerTextField: @Composable () -> Unit,
+        enabled: Boolean,
+        singleLine: Boolean,
+        visualTransformation: VisualTransformation,
+        interactionSource: InteractionSource,
+        isError: Boolean = false,
+        label: @Composable (() -> Unit)? = null,
+        placeholder: @Composable (() -> Unit)? = null,
+        leadingIcon: @Composable (() -> Unit)? = null,
+        trailingIcon: @Composable (() -> Unit)? = null,
+        colors: TextFieldColors = outlinedTextFieldColors(),
+        contentPadding: PaddingValues = outlinedTextFieldPadding(),
+        border: @Composable () -> Unit = {
+            BorderStroke(enabled, isError, interactionSource, colors)
+        }
+    ) {
+        CommonDecorationBox(
+            type = TextFieldType.Outlined,
+            value = value,
+            visualTransformation = visualTransformation,
+            innerTextField = innerTextField,
+            placeholder = placeholder,
+            label = label,
+            leadingIcon = leadingIcon,
+            trailingIcon = trailingIcon,
+            singleLine = singleLine,
+            enabled = enabled,
+            isError = isError,
+            interactionSource = interactionSource,
+            colors = colors,
+            contentPadding = contentPadding,
+            border = border
+        )
+    }
 }
 
 @Immutable
@@ -474,4 +828,26 @@
         result = 31 * result + disabledPlaceholderColor.hashCode()
         return result
     }
+}
+
+@Composable
+private fun animateBorderStrokeAsState(
+    enabled: Boolean,
+    isError: Boolean,
+    interactionSource: InteractionSource,
+    colors: TextFieldColors,
+    focusedBorderThickness: Dp,
+    unfocusedBorderThickness: Dp
+): State<BorderStroke> {
+    val focused by interactionSource.collectIsFocusedAsState()
+    val indicatorColor = colors.indicatorColor(enabled, isError, interactionSource)
+    val targetThickness = if (focused) focusedBorderThickness else unfocusedBorderThickness
+    val animatedThickness = if (enabled) {
+        animateDpAsState(targetThickness, tween(durationMillis = AnimationDuration))
+    } else {
+        rememberUpdatedState(unfocusedBorderThickness)
+    }
+    return rememberUpdatedState(
+        BorderStroke(animatedThickness.value, SolidColor(indicatorColor.value))
+    )
 }
\ No newline at end of file
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt
index 82f277b..31a4468 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt
@@ -18,38 +18,37 @@
 
 import androidx.compose.animation.animateColor
 import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.animateDp
 import androidx.compose.animation.core.animateFloat
 import androidx.compose.animation.core.spring
 import androidx.compose.animation.core.tween
 import androidx.compose.animation.core.updateTransition
-import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.collectIsFocusedAsState
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.defaultMinSize
-import androidx.compose.foundation.text.KeyboardActions
-import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material.Strings.Companion.DefaultErrorMessage
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.layout.IntrinsicMeasurable
 import androidx.compose.ui.layout.LayoutIdParentData
 import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.layoutId
 import androidx.compose.ui.semantics.error
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.text.lerp
 import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 
 internal enum class TextFieldType {
@@ -61,38 +60,28 @@
  */
 @OptIn(ExperimentalMaterialApi::class)
 @Composable
-internal fun TextFieldImpl(
+internal fun CommonDecorationBox(
     type: TextFieldType,
-    enabled: Boolean,
-    readOnly: Boolean,
-    value: TextFieldValue,
-    onValueChange: (TextFieldValue) -> Unit,
-    modifier: Modifier,
-    singleLine: Boolean,
-    textStyle: TextStyle,
-    label: @Composable (() -> Unit)?,
-    placeholder: @Composable (() -> Unit)?,
-    leading: @Composable (() -> Unit)?,
-    trailing: @Composable (() -> Unit)?,
-    isError: Boolean,
+    value: String,
+    innerTextField: @Composable () -> Unit,
     visualTransformation: VisualTransformation,
-    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
-    keyboardActions: KeyboardActions,
-    maxLines: Int = Int.MAX_VALUE,
-    interactionSource: MutableInteractionSource,
-    shape: Shape,
-    colors: TextFieldColors
+    label: @Composable (() -> Unit)?,
+    placeholder: @Composable (() -> Unit)? = null,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    singleLine: Boolean = false,
+    enabled: Boolean = true,
+    isError: Boolean = false,
+    interactionSource: InteractionSource,
+    contentPadding: PaddingValues,
+    colors: TextFieldColors,
+    border: @Composable (() -> Unit)? = null
 ) {
-    // If color is not provided via the text style, use content color as a default
-    val textColor = textStyle.color.takeOrElse {
-        colors.textColor(enabled).value
-    }
-    val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
+    val transformedText = remember(value, visualTransformation) {
+        visualTransformation.filter(AnnotatedString(value))
+    }.text.text
 
     val isFocused = interactionSource.collectIsFocusedAsState().value
-    val transformedText = remember(value.annotatedString, visualTransformation) {
-        visualTransformation.filter(value.annotatedString)
-    }.text
     val inputState = when {
         isFocused -> InputPhase.Focused
         transformedText.isEmpty() -> InputPhase.UnfocusedEmpty
@@ -126,8 +115,7 @@
         },
         contentColor = labelColor,
         showLabel = label != null
-    ) { labelProgress, labelTextStyleColor, labelContentColor, indicatorWidth,
-        placeholderAlphaProgress ->
+    ) { labelProgress, labelTextStyleColor, labelContentColor, placeholderAlphaProgress ->
 
         val decoratedLabel: @Composable (() -> Unit)? = label?.let {
             @Composable {
@@ -159,77 +147,76 @@
         // message slot API, we can set the default error message in case developers forget about
         // it.
         val defaultErrorMessage = getString(DefaultErrorMessage)
-        val textFieldModifier = modifier.semantics { if (isError) error(defaultErrorMessage) }
+        val decorationBoxModifier = Modifier.semantics { if (isError) error(defaultErrorMessage) }
 
         val leadingIconColor = if (colors is TextFieldColorsWithIcons) {
             colors.leadingIconColor(enabled, isError, interactionSource).value
         } else {
             colors.leadingIconColor(enabled, isError).value
         }
+        val decoratedLeading: @Composable (() -> Unit)? = leadingIcon?.let {
+            @Composable {
+                Decoration(contentColor = leadingIconColor, content = it)
+            }
+        }
 
         val trailingIconColor = if (colors is TextFieldColorsWithIcons) {
             colors.trailingIconColor(enabled, isError, interactionSource).value
         } else {
             colors.trailingIconColor(enabled, isError).value
         }
+        val decoratedTrailing: @Composable (() -> Unit)? = trailingIcon?.let {
+            @Composable {
+                Decoration(contentColor = trailingIconColor, content = it)
+            }
+        }
 
         when (type) {
             TextFieldType.Filled -> {
                 TextFieldLayout(
-                    modifier = textFieldModifier,
-                    value = value,
-                    onValueChange = onValueChange,
-                    enabled = enabled,
-                    readOnly = readOnly,
-                    keyboardOptions = keyboardOptions,
-                    keyboardActions = keyboardActions,
-                    textStyle = mergedTextStyle,
+                    modifier = decorationBoxModifier,
+                    textField = innerTextField,
+                    placeholder = decoratedPlaceholder,
+                    label = decoratedLabel,
+                    leading = decoratedLeading,
+                    trailing = decoratedTrailing,
                     singleLine = singleLine,
-                    maxLines = maxLines,
-                    visualTransformation = visualTransformation,
-                    interactionSource = interactionSource,
-                    decoratedPlaceholder = decoratedPlaceholder,
-                    decoratedLabel = decoratedLabel,
-                    leading = leading,
-                    trailing = trailing,
-                    leadingColor = leadingIconColor,
-                    trailingColor = trailingIconColor,
-                    labelProgress = labelProgress,
-                    indicatorWidth = indicatorWidth,
-                    indicatorColor =
-                        colors.indicatorColor(enabled, isError, interactionSource).value,
-                    backgroundColor = colors.backgroundColor(enabled).value,
-                    cursorColor = colors.cursorColor(isError).value,
-                    shape = shape
+                    animationProgress = labelProgress,
+                    paddingValues = contentPadding
                 )
             }
             TextFieldType.Outlined -> {
+                // Outlined cutout
+                val labelSize = remember { mutableStateOf(Size.Zero) }
+                val drawBorder: @Composable () -> Unit = {
+                    Box(
+                        Modifier.layoutId(BorderId).outlineCutout(labelSize.value, contentPadding),
+                        propagateMinConstraints = true
+                    ) {
+                        border?.invoke()
+                    }
+                }
+
                 OutlinedTextFieldLayout(
-                    modifier = textFieldModifier,
-                    value = value,
-                    onValueChange = onValueChange,
-                    enabled = enabled,
-                    readOnly = readOnly,
-                    keyboardOptions = keyboardOptions,
-                    keyboardActions = keyboardActions,
-                    textStyle = mergedTextStyle,
+                    modifier = decorationBoxModifier,
+                    textField = innerTextField,
+                    placeholder = decoratedPlaceholder,
+                    label = decoratedLabel,
+                    leading = decoratedLeading,
+                    trailing = decoratedTrailing,
                     singleLine = singleLine,
-                    maxLines = maxLines,
-                    visualTransformation = visualTransformation,
-                    interactionSource = interactionSource,
-                    decoratedPlaceholder = decoratedPlaceholder,
-                    decoratedLabel = decoratedLabel,
-                    leading = leading,
-                    trailing = trailing,
-                    leadingColor = leadingIconColor,
-                    trailingColor = trailingIconColor,
-                    labelProgress = labelProgress,
-                    indicatorWidth = indicatorWidth,
-                    indicatorColor =
-                        colors.indicatorColor(enabled, isError, interactionSource).value,
-                    shape = shape,
-                    backgroundColor = colors.backgroundColor(enabled).value,
-                    cursorColor = colors.cursorColor(isError).value
+                    onLabelMeasured = {
+                        val labelWidth = it.width * labelProgress
+                        val labelHeight = it.height * labelProgress
+                        if (labelSize.value.width != labelWidth ||
+                            labelSize.value.height != labelHeight
+                        ) {
+                            labelSize.value = Size(labelWidth, labelHeight)
+                        }
+                    },
+                    animationProgress = labelProgress,
+                    border = drawBorder,
+                    paddingValues = contentPadding
                 )
             }
         }
@@ -279,7 +266,6 @@
             labelProgress: Float,
             labelTextStyleColor: Color,
             labelContentColor: Color,
-            indicatorWidth: Dp,
             placeholderOpacity: Float
         ) -> Unit
     ) {
@@ -299,17 +285,6 @@
             }
         }
 
-        val indicatorWidth by transition.animateDp(
-            label = "IndicatorWidth",
-            transitionSpec = { tween(durationMillis = AnimationDuration) }
-        ) {
-            when (it) {
-                InputPhase.Focused -> IndicatorFocusedWidth
-                InputPhase.UnfocusedEmpty -> IndicatorUnfocusedWidth
-                InputPhase.UnfocusedNotEmpty -> IndicatorUnfocusedWidth
-            }
-        }
-
         val placeholderOpacity by transition.animateFloat(
             label = "PlaceholderOpacity",
             transitionSpec = {
@@ -358,7 +333,6 @@
             labelProgress,
             labelTextStyleColor,
             labelContentColor,
-            indicatorWidth,
             placeholderOpacity
         )
     }
@@ -392,8 +366,6 @@
 private const val PlaceholderAnimationDuration = 83
 private const val PlaceholderAnimationDelayOrDuration = 67
 
-private val IndicatorUnfocusedWidth = 1.dp
-private val IndicatorFocusedWidth = 2.dp
 internal val TextFieldPadding = 16.dp
 internal val HorizontalIconPadding = 12.dp
 
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index fcfc11e..6e17626 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -8,6 +8,11 @@
     method @androidx.compose.runtime.Composable public static void AlertDialog(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function0<kotlin.Unit> confirmButton, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional float tonalElevation, optional long iconContentColor, optional long titleContentColor, optional long textContentColor, optional androidx.compose.ui.window.DialogProperties properties);
   }
 
+  public final class AndroidMenu_androidKt {
+    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
   public final class AppBarKt {
     method @androidx.compose.runtime.Composable public static void CenterAlignedTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
     method @androidx.compose.runtime.Composable public static void LargeTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
@@ -218,6 +223,22 @@
     method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.compose.material3.ColorScheme colorScheme, optional androidx.compose.material3.Typography typography, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public final class MenuDefaults {
+    method public androidx.compose.foundation.layout.PaddingValues getDropdownMenuItemContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.MenuItemColors itemColors(optional long textColor, optional long leadingIconColor, optional long trailingIconColor, optional long disabledTextColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
+    property public final androidx.compose.foundation.layout.PaddingValues DropdownMenuItemContentPadding;
+    field public static final androidx.compose.material3.MenuDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public interface MenuItemColors {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> textColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trailingIconColor(boolean enabled);
+  }
+
+  public final class MenuKt {
+  }
+
   @androidx.compose.runtime.Stable public interface NavigationBarItemColors {
     method @androidx.compose.runtime.Composable public long getIndicatorColor();
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> iconColor(boolean selected);
@@ -333,7 +354,7 @@
 
   public final class SurfaceKt {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.InteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteTonalElevation();
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
   }
@@ -341,6 +362,33 @@
   public final class SwipeableKt {
   }
 
+  public final class TabKt {
+    method @androidx.compose.runtime.Composable public static void LeadingIconTab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TabPosition {
+    method public float getLeft();
+    method public float getRight();
+    method public float getWidth();
+    property public final float left;
+    property public final float right;
+    property public final float width;
+  }
+
+  public final class TabRowDefaults {
+    method @androidx.compose.runtime.Composable public void Divider(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+    method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material3.TabPosition currentTabPosition);
+    field public static final androidx.compose.material3.TabRowDefaults INSTANCE;
+  }
+
+  public final class TabRowKt {
+    method @androidx.compose.runtime.Composable public static void ScrollableTabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float edgePadding, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+    method @androidx.compose.runtime.Composable public static void TabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+  }
+
   public final class TextKt {
     method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index 4f97112..988bc6f 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -8,6 +8,11 @@
     method @androidx.compose.runtime.Composable public static void AlertDialog(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function0<kotlin.Unit> confirmButton, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional float tonalElevation, optional long iconContentColor, optional long titleContentColor, optional long textContentColor, optional androidx.compose.ui.window.DialogProperties properties);
   }
 
+  public final class AndroidMenu_androidKt {
+    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
   public final class AppBarKt {
     method @androidx.compose.runtime.Composable public static void CenterAlignedTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
     method @androidx.compose.runtime.Composable public static void LargeTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
@@ -274,6 +279,22 @@
     method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.compose.material3.ColorScheme colorScheme, optional androidx.compose.material3.Typography typography, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public final class MenuDefaults {
+    method public androidx.compose.foundation.layout.PaddingValues getDropdownMenuItemContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.MenuItemColors itemColors(optional long textColor, optional long leadingIconColor, optional long trailingIconColor, optional long disabledTextColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
+    property public final androidx.compose.foundation.layout.PaddingValues DropdownMenuItemContentPadding;
+    field public static final androidx.compose.material3.MenuDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public interface MenuItemColors {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> textColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trailingIconColor(boolean enabled);
+  }
+
+  public final class MenuKt {
+  }
+
   @androidx.compose.runtime.Stable public interface NavigationBarItemColors {
     method @androidx.compose.runtime.Composable public long getIndicatorColor();
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> iconColor(boolean selected);
@@ -394,7 +415,7 @@
 
   public final class SurfaceKt {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.InteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteTonalElevation();
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
   }
@@ -402,6 +423,33 @@
   public final class SwipeableKt {
   }
 
+  public final class TabKt {
+    method @androidx.compose.runtime.Composable public static void LeadingIconTab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TabPosition {
+    method public float getLeft();
+    method public float getRight();
+    method public float getWidth();
+    property public final float left;
+    property public final float right;
+    property public final float width;
+  }
+
+  public final class TabRowDefaults {
+    method @androidx.compose.runtime.Composable public void Divider(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+    method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material3.TabPosition currentTabPosition);
+    field public static final androidx.compose.material3.TabRowDefaults INSTANCE;
+  }
+
+  public final class TabRowKt {
+    method @androidx.compose.runtime.Composable public static void ScrollableTabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float edgePadding, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+    method @androidx.compose.runtime.Composable public static void TabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+  }
+
   public final class TextKt {
     method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index fcfc11e..6e17626 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -8,6 +8,11 @@
     method @androidx.compose.runtime.Composable public static void AlertDialog(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function0<kotlin.Unit> confirmButton, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional float tonalElevation, optional long iconContentColor, optional long titleContentColor, optional long textContentColor, optional androidx.compose.ui.window.DialogProperties properties);
   }
 
+  public final class AndroidMenu_androidKt {
+    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
   public final class AppBarKt {
     method @androidx.compose.runtime.Composable public static void CenterAlignedTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
     method @androidx.compose.runtime.Composable public static void LargeTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
@@ -218,6 +223,22 @@
     method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.compose.material3.ColorScheme colorScheme, optional androidx.compose.material3.Typography typography, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public final class MenuDefaults {
+    method public androidx.compose.foundation.layout.PaddingValues getDropdownMenuItemContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.MenuItemColors itemColors(optional long textColor, optional long leadingIconColor, optional long trailingIconColor, optional long disabledTextColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
+    property public final androidx.compose.foundation.layout.PaddingValues DropdownMenuItemContentPadding;
+    field public static final androidx.compose.material3.MenuDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public interface MenuItemColors {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> textColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trailingIconColor(boolean enabled);
+  }
+
+  public final class MenuKt {
+  }
+
   @androidx.compose.runtime.Stable public interface NavigationBarItemColors {
     method @androidx.compose.runtime.Composable public long getIndicatorColor();
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> iconColor(boolean selected);
@@ -333,7 +354,7 @@
 
   public final class SurfaceKt {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.InteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteTonalElevation();
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
   }
@@ -341,6 +362,33 @@
   public final class SwipeableKt {
   }
 
+  public final class TabKt {
+    method @androidx.compose.runtime.Composable public static void LeadingIconTab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TabPosition {
+    method public float getLeft();
+    method public float getRight();
+    method public float getWidth();
+    property public final float left;
+    property public final float right;
+    property public final float width;
+  }
+
+  public final class TabRowDefaults {
+    method @androidx.compose.runtime.Composable public void Divider(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+    method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material3.TabPosition currentTabPosition);
+    field public static final androidx.compose.material3.TabRowDefaults INSTANCE;
+  }
+
+  public final class TabRowKt {
+    method @androidx.compose.runtime.Composable public static void ScrollableTabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float edgePadding, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+    method @androidx.compose.runtime.Composable public static void TabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+  }
+
   public final class TextKt {
     method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt
index 3d50af6..68fbfa2 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt
@@ -131,6 +131,18 @@
     examples = FloatingActionButtonsExamples,
 )
 
+private val Menus = Component(
+    id = nextId(),
+    name = "Menus",
+    description = "Menus display a list of choices on temporary surfaces.",
+    // No menu icon
+    tintIcon = true,
+    guidelinesUrl = "$ComponentGuidelinesUrl/menus",
+    docsUrl = "$PackageSummaryUrl#dropdownmenu",
+    sourceUrl = "$Material3SourceUrl/Menu.kt",
+    examples = MenusExamples
+)
+
 private val NavigationBar = Component(
     id = nextId(),
     name = "Navigation bar",
@@ -207,6 +219,18 @@
     examples = SnackbarsExamples
 )
 
+private val Tabs = Component(
+    id = nextId(),
+    name = "Tabs",
+    description = "Tabs organize content across different screens, data sets, and other " +
+        "interactions.",
+    // No tabs icon
+    guidelinesUrl = "$ComponentGuidelinesUrl/tabs",
+    docsUrl = "$DocsUrl#tab",
+    sourceUrl = "$Material3SourceUrl/Tab.kt",
+    examples = TabsExamples
+)
+
 private val TopAppBar = Component(
     id = nextId(),
     name = "Top app bar",
@@ -228,11 +252,13 @@
     Dialogs,
     ExtendedFloatingActionButton,
     FloatingActionButtons,
+    Menus,
     NavigationBar,
     NavigationDrawer,
     NavigationRail,
     ProgressIndicators,
     RadioButtons,
     Snackbars,
+    Tabs,
     TopAppBar
 )
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
index 450590ab..1165253 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
@@ -36,10 +36,16 @@
 import androidx.compose.material3.samples.ExitUntilCollapsedLargeTopAppBar
 import androidx.compose.material3.samples.ExitUntilCollapsedMediumTopAppBar
 import androidx.compose.material3.samples.ExtendedFloatingActionButtonSample
+import androidx.compose.material3.samples.FancyTabs
+import androidx.compose.material3.samples.FancyIndicatorTabs
+import androidx.compose.material3.samples.FancyIndicatorContainerTabs
 import androidx.compose.material3.samples.FilledTonalButtonSample
 import androidx.compose.material3.samples.FloatingActionButtonSample
+import androidx.compose.material3.samples.IconTabs
 import androidx.compose.material3.samples.LargeFloatingActionButtonSample
 import androidx.compose.material3.samples.LinearProgressIndicatorSample
+import androidx.compose.material3.samples.LeadingIconTabs
+import androidx.compose.material3.samples.MenuSample
 import androidx.compose.material3.samples.NavigationBarSample
 import androidx.compose.material3.samples.NavigationBarWithOnlySelectedLabelsSample
 import androidx.compose.material3.samples.NavigationDrawerSample
@@ -55,10 +61,14 @@
 import androidx.compose.material3.samples.ScaffoldWithCustomSnackbar
 import androidx.compose.material3.samples.ScaffoldWithIndefiniteSnackbar
 import androidx.compose.material3.samples.ScaffoldWithSimpleSnackbar
+import androidx.compose.material3.samples.ScrollingFancyIndicatorContainerTabs
+import androidx.compose.material3.samples.ScrollingTextTabs
 import androidx.compose.material3.samples.SimpleCenterAlignedTopAppBar
 import androidx.compose.material3.samples.SimpleSmallTopAppBar
 import androidx.compose.material3.samples.SmallFloatingActionButtonSample
+import androidx.compose.material3.samples.TextAndIconTabs
 import androidx.compose.material3.samples.TextButtonSample
+import androidx.compose.material3.samples.TextTabs
 import androidx.compose.material3.samples.TriStateCheckboxSample
 import androidx.compose.runtime.Composable
 
@@ -267,6 +277,18 @@
         ) { SmallFloatingActionButtonSample() }
     )
 
+private const val MenusExampleDescription = "Menus examples"
+private const val MenusExampleSourceUrl = "$SampleSourceUrl/MenuSamples.kt"
+val MenusExamples = listOf(
+    Example(
+        name = ::MenuSample.name,
+        description = MenusExampleDescription,
+        sourceUrl = MenusExampleSourceUrl
+    ) {
+        MenuSample()
+    }
+)
+
 private const val NavigationBarExampleDescription = "Navigation bar examples"
 private const val NavigationBarExampleSourceUrl = "$SampleSourceUrl/NavigationBarSamples.kt"
 val NavigationBarExamples =
@@ -387,3 +409,71 @@
         ScaffoldWithCoroutinesSnackbar()
     }
 )
+
+private const val TabsExampleDescription = "Tabs examples"
+private const val TabsExampleSourceUrl = "$SampleSourceUrl/TabSamples.kt"
+val TabsExamples = listOf(
+    Example(
+        name = ::TextTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        TextTabs()
+    },
+    Example(
+        name = ::IconTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        IconTabs()
+    },
+    Example(
+        name = ::TextAndIconTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        TextAndIconTabs()
+    },
+    Example(
+        name = ::LeadingIconTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        LeadingIconTabs()
+    },
+    Example(
+        name = ::ScrollingTextTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        ScrollingTextTabs()
+    },
+    Example(
+        name = ::FancyTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        FancyTabs()
+    },
+    Example(
+        name = ::FancyIndicatorTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        FancyIndicatorTabs()
+    },
+    Example(
+        name = ::FancyIndicatorContainerTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        FancyIndicatorContainerTabs()
+    },
+    Example(
+        name = ::ScrollingFancyIndicatorContainerTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        ScrollingFancyIndicatorContainerTabs()
+    }
+)
\ No newline at end of file
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/component/ComponentItem.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/component/ComponentItem.kt
index 0576bae..f399060 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/component/ComponentItem.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/component/ComponentItem.kt
@@ -16,19 +16,22 @@
 
 package androidx.compose.material3.catalog.library.ui.component
 
-import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
+import androidx.compose.material3.OutlinedCard
 import androidx.compose.material3.Text
 import androidx.compose.material3.catalog.library.model.Component
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.ColorFilter
@@ -36,24 +39,24 @@
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.unit.dp
 
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun ComponentItem(
     component: Component,
     onClick: (component: Component) -> Unit
 ) {
-    // TODO: Replace with M3 Card when available
-    Surface(
-        onClick = { onClick(component) },
+    val interactionSource = remember { MutableInteractionSource() }
+    OutlinedCard(
         modifier = Modifier
             .height(ComponentItemHeight)
-            .padding(ComponentItemOuterPadding),
-        shape = ComponentItemShape,
-        border = BorderStroke(
-            width = ComponentItemBorderWidth,
-            color = MaterialTheme.colorScheme.outline
-        )
+            .padding(ComponentItemOuterPadding)
+            .clickable(
+                interactionSource = interactionSource,
+                indication = null,
+                onClick = { onClick(component) }),
+        interactionSource = interactionSource
     ) {
-        Box(modifier = Modifier.padding(ComponentItemInnerPadding)) {
+        Box(modifier = Modifier.fillMaxSize().padding(ComponentItemInnerPadding)) {
             Image(
                 painter = painterResource(id = component.icon),
                 contentDescription = null,
@@ -80,5 +83,3 @@
 private val ComponentItemOuterPadding = 4.dp
 private val ComponentItemInnerPadding = 16.dp
 private val ComponentItemIconSize = 80.dp
-private val ComponentItemBorderWidth = 1.dp
-private val ComponentItemShape = RoundedCornerShape(12.dp)
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/example/ExampleItem.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/example/ExampleItem.kt
index e8d005e..807d84a 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/example/ExampleItem.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/example/ExampleItem.kt
@@ -16,7 +16,8 @@
 
 package androidx.compose.material3.catalog.library.ui.example
 
-import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
@@ -24,33 +25,35 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.KeyboardArrowRight
+import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
+import androidx.compose.material3.OutlinedCard
 import androidx.compose.material3.Text
 import androidx.compose.material3.catalog.library.model.Example
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun ExampleItem(
     example: Example,
     onClick: (example: Example) -> Unit
 ) {
-    // TODO: Replace with M3 Card when available
-    Surface(
-        onClick = { onClick(example) },
-        modifier = Modifier.fillMaxWidth(),
-        shape = ExampleItemShape,
-        border = BorderStroke(
-            width = ExampleItemBorderWidth,
-            color = MaterialTheme.colorScheme.outline
-        )
+    val interactionSource = remember { MutableInteractionSource() }
+    OutlinedCard(
+        modifier = Modifier
+            .fillMaxWidth()
+            .clickable(
+                interactionSource = interactionSource,
+                indication = null,
+                onClick = { onClick(example) }),
+        interactionSource = interactionSource
     ) {
         Row(modifier = Modifier.padding(ExampleItemPadding)) {
             Column(modifier = Modifier.weight(1f, fill = true)) {
@@ -76,5 +79,3 @@
 
 private val ExampleItemPadding = 16.dp
 private val ExampleItemTextPadding = 8.dp
-private val ExampleItemBorderWidth = 1.dp
-private val ExampleItemShape = RoundedCornerShape(12.dp)
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
new file mode 100644
index 0000000..2455922
--- /dev/null
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2022 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.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.MoreVert
+import androidx.compose.material.icons.outlined.Edit
+import androidx.compose.material.icons.outlined.Email
+import androidx.compose.material.icons.outlined.Settings
+import androidx.compose.material3.Divider
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Text
+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
+
+@Sampled
+@Composable
+fun MenuSample() {
+    var expanded by remember { mutableStateOf(false) }
+
+    Box(modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.TopStart)) {
+        IconButton(onClick = { expanded = true }) {
+            Icon(Icons.Default.MoreVert, contentDescription = "Localized description")
+        }
+        DropdownMenu(
+            expanded = expanded,
+            onDismissRequest = { expanded = false }
+        ) {
+            DropdownMenuItem(
+                text = { Text("Edit") },
+                onClick = { /* Handle edit! */ },
+                leadingIcon = {
+                    Icon(
+                        Icons.Outlined.Edit,
+                        contentDescription = null
+                    )
+                })
+            DropdownMenuItem(
+                text = { Text("Settings") },
+                onClick = { /* Handle settings! */ },
+                leadingIcon = {
+                    Icon(
+                        Icons.Outlined.Settings,
+                        contentDescription = null
+                    )
+                })
+            Divider()
+            DropdownMenuItem(
+                text = { Text("Send Feedback") },
+                onClick = { /* Handle send feedback! */ },
+                leadingIcon = {
+                    Icon(
+                        Icons.Outlined.Email,
+                        contentDescription = null
+                    )
+                },
+                trailingIcon = { Text("F11", textAlign = TextAlign.Center) })
+        }
+    }
+}
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TabSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TabSamples.kt
new file mode 100644
index 0000000..44ae71a
--- /dev/null
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TabSamples.kt
@@ -0,0 +1,400 @@
+/*
+ * Copyright 2022 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.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.animation.animateColor
+import androidx.compose.animation.core.animateDp
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ScrollableTabRow
+import androidx.compose.material3.Tab
+import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
+import androidx.compose.material3.TabPosition
+import androidx.compose.material3.TabRow
+import androidx.compose.material3.LeadingIconTab
+import androidx.compose.material3.Text
+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.graphics.Color
+import androidx.compose.ui.unit.dp
+
+@Sampled
+@Composable
+fun TextTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titles = listOf("TAB 1", "TAB 2", "TAB 3 WITH LOTS OF TEXT")
+    Column {
+        TabRow(selectedTabIndex = state) {
+            titles.forEachIndexed { index, title ->
+                Tab(
+                    text = { Text(title) },
+                    selected = state == index,
+                    onClick = { state = index }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Text tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Composable
+fun IconTabs() {
+    var state by remember { mutableStateOf(0) }
+    val icons = listOf(Icons.Filled.Favorite, Icons.Filled.Favorite, Icons.Filled.Favorite)
+    Column {
+        TabRow(selectedTabIndex = state) {
+            icons.forEachIndexed { index, icon ->
+                Tab(
+                    icon = { Icon(icon, contentDescription = "Favorite") },
+                    selected = state == index,
+                    onClick = { state = index }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Icon tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Composable
+fun TextAndIconTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titlesAndIcons = listOf(
+        "TAB 1" to Icons.Filled.Favorite,
+        "TAB 2" to Icons.Filled.Favorite,
+        "TAB 3 WITH LOTS OF TEXT" to Icons.Filled.Favorite
+    )
+    Column {
+        TabRow(selectedTabIndex = state) {
+            titlesAndIcons.forEachIndexed { index, (title, icon) ->
+                Tab(
+                    text = { Text(title) },
+                    icon = { Icon(icon, contentDescription = null) },
+                    selected = state == index,
+                    onClick = { state = index }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Text and icon tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Composable
+fun LeadingIconTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titlesAndIcons = listOf(
+        "TAB" to Icons.Filled.Favorite,
+        "TAB & ICON" to Icons.Filled.Favorite,
+        "TAB 3 WITH LOTS OF TEXT" to Icons.Filled.Favorite
+    )
+    Column {
+        TabRow(selectedTabIndex = state) {
+            titlesAndIcons.forEachIndexed { index, (title, icon) ->
+                LeadingIconTab(
+                    text = { Text(title) },
+                    icon = { Icon(icon, contentDescription = null) },
+                    selected = state == index,
+                    onClick = { state = index }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Leading icon tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Composable
+fun ScrollingTextTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titles = listOf(
+        "TAB 1",
+        "TAB 2",
+        "TAB 3 WITH LOTS OF TEXT",
+        "TAB 4",
+        "TAB 5",
+        "TAB 6 WITH LOTS OF TEXT",
+        "TAB 7",
+        "TAB 8",
+        "TAB 9 WITH LOTS OF TEXT",
+        "TAB 10"
+    )
+    Column {
+        ScrollableTabRow(selectedTabIndex = state) {
+            titles.forEachIndexed { index, title ->
+                Tab(
+                    text = { Text(title) },
+                    selected = state == index,
+                    onClick = { state = index }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Scrolling text tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Sampled
+@Composable
+fun FancyTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titles = listOf("TAB 1", "TAB 2", "TAB 3")
+    Column {
+        TabRow(selectedTabIndex = state) {
+            titles.forEachIndexed { index, title ->
+                FancyTab(title = title, onClick = { state = index }, selected = (index == state))
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Fancy tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Sampled
+@Composable
+fun FancyIndicatorTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titles = listOf("TAB 1", "TAB 2", "TAB 3")
+
+    // Reuse the default offset animation modifier, but use our own indicator
+    val indicator = @Composable { tabPositions: List<TabPosition> ->
+        FancyIndicator(Color.White, Modifier.tabIndicatorOffset(tabPositions[state]))
+    }
+
+    Column {
+        TabRow(
+            selectedTabIndex = state,
+            indicator = indicator
+        ) {
+            titles.forEachIndexed { index, title ->
+                Tab(
+                    text = { Text(title) },
+                    selected = state == index,
+                    onClick = { state = index }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Fancy indicator tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Sampled
+@Composable
+fun FancyIndicatorContainerTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titles = listOf("TAB 1", "TAB 2", "TAB 3")
+
+    val indicator = @Composable { tabPositions: List<TabPosition> ->
+        FancyAnimatedIndicator(tabPositions = tabPositions, selectedTabIndex = state)
+    }
+
+    Column {
+        TabRow(
+            selectedTabIndex = state,
+            indicator = indicator
+        ) {
+            titles.forEachIndexed { index, title ->
+                Tab(
+                    text = { Text(title) },
+                    selected = state == index,
+                    onClick = { state = index }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Fancy transition tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Composable
+fun ScrollingFancyIndicatorContainerTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titles = listOf(
+        "TAB 1",
+        "TAB 2",
+        "TAB 3 WITH LOTS OF TEXT",
+        "TAB 4",
+        "TAB 5",
+        "TAB 6 WITH LOTS OF TEXT",
+        "TAB 7",
+        "TAB 8",
+        "TAB 9 WITH LOTS OF TEXT",
+        "TAB 10"
+    )
+    val indicator = @Composable { tabPositions: List<TabPosition> ->
+        FancyAnimatedIndicator(tabPositions = tabPositions, selectedTabIndex = state)
+    }
+
+    Column {
+        ScrollableTabRow(
+            selectedTabIndex = state,
+            indicator = indicator
+        ) {
+            titles.forEachIndexed { index, title ->
+                Tab(
+                    text = { Text(title) },
+                    selected = state == index,
+                    onClick = { state = index }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Scrolling fancy transition tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Sampled
+@Composable
+fun FancyTab(title: String, onClick: () -> Unit, selected: Boolean) {
+    Tab(selected, onClick) {
+        Column(
+            Modifier.padding(10.dp).height(50.dp).fillMaxWidth(),
+            verticalArrangement = Arrangement.SpaceBetween
+        ) {
+            Box(
+                Modifier.size(10.dp)
+                    .align(Alignment.CenterHorizontally)
+                    .background(color = if (selected) Color.Red else Color.White)
+            )
+            Text(
+                text = title,
+                style = MaterialTheme.typography.bodyLarge,
+                modifier = Modifier.align(Alignment.CenterHorizontally)
+            )
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun FancyIndicator(color: Color, modifier: Modifier = Modifier) {
+    // Draws a rounded rectangular with border around the Tab, with a 5.dp padding from the edges
+    // Color is passed in as a parameter [color]
+    Box(
+        modifier
+            .padding(5.dp)
+            .fillMaxSize()
+            .border(BorderStroke(2.dp, color), RoundedCornerShape(5.dp))
+    )
+}
+
+@Sampled
+@Composable
+fun FancyAnimatedIndicator(tabPositions: List<TabPosition>, selectedTabIndex: Int) {
+    val colors = listOf(Color.Yellow, Color.Red, Color.Green)
+    val transition = updateTransition(selectedTabIndex)
+    val indicatorStart by transition.animateDp(
+        transitionSpec = {
+            // Handle directionality here, if we are moving to the right, we
+            // want the right side of the indicator to move faster, if we are
+            // moving to the left, we want the left side to move faster.
+            if (initialState < targetState) {
+                spring(dampingRatio = 1f, stiffness = 50f)
+            } else {
+                spring(dampingRatio = 1f, stiffness = 1000f)
+            }
+        }
+    ) {
+        tabPositions[it].left
+    }
+
+    val indicatorEnd by transition.animateDp(
+        transitionSpec = {
+            // Handle directionality here, if we are moving to the right, we
+            // want the right side of the indicator to move faster, if we are
+            // moving to the left, we want the left side to move faster.
+            if (initialState < targetState) {
+                spring(dampingRatio = 1f, stiffness = 1000f)
+            } else {
+                spring(dampingRatio = 1f, stiffness = 50f)
+            }
+        }
+    ) {
+        tabPositions[it].right
+    }
+
+    val indicatorColor by transition.animateColor {
+        colors[it % colors.size]
+    }
+
+    FancyIndicator(
+        // Pass the current color to the indicator
+        indicatorColor,
+        modifier = Modifier
+            // Fill up the entire TabRow, and place the indicator at the start
+            .fillMaxSize()
+            .wrapContentSize(align = Alignment.BottomStart)
+            // Apply an offset from the start to correctly position the indicator around the tab
+            .offset(x = indicatorStart)
+            // Make the width of the indicator follow the animated width as we move between tabs
+            .width(indicatorEnd - indicatorStart)
+    )
+}
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
new file mode 100644
index 0000000..ea3f79f
--- /dev/null
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2022 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
+
+import android.os.Build
+import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Edit
+import androidx.compose.material.icons.outlined.Email
+import androidx.compose.material.icons.outlined.Lock
+import androidx.compose.material.icons.outlined.Settings
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Screenshot tests for the Material Menus.
+ *
+ * Note that currently nodes in a popup cannot be captured to bitmaps. A [DropdownMenu] is
+ * displaying its content in a popup, so the tests here focus on the [DropdownMenuContent].
+ */
+// TODO(b/208991956): Update to include DropdownMenu when popups can be captured into bitmaps.
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+class MenuScreenshotTest {
+
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL3)
+
+    private val testTag = "dropdown_menu"
+
+    @Test
+    fun dropdownMenu_lightTheme() {
+        composeTestRule.setMaterialContent(lightColorScheme()) {
+            TestMenu(enabledItems = true)
+        }
+        assertAgainstGolden(goldenIdentifier = "dropdownMenu_lightTheme")
+    }
+
+    @Test
+    fun dropdownMenu_darkTheme() {
+        composeTestRule.setMaterialContent(darkColorScheme()) {
+            TestMenu(enabledItems = true)
+        }
+        assertAgainstGolden(goldenIdentifier = "dropdownMenu_darkTheme")
+    }
+
+    @Test
+    fun dropdownMenu_disabled_lightTheme() {
+        composeTestRule.setMaterialContent(lightColorScheme()) {
+            TestMenu(enabledItems = false)
+        }
+        assertAgainstGolden(goldenIdentifier = "dropdownMenu_disabled_lightTheme")
+    }
+
+    @Test
+    fun dropdownMenu_disabled_darkTheme() {
+        composeTestRule.setMaterialContent(darkColorScheme()) {
+            TestMenu(enabledItems = false)
+        }
+        assertAgainstGolden(goldenIdentifier = "dropdownMenu_disabled_darkTheme")
+    }
+
+    @Composable
+    private fun TestMenu(enabledItems: Boolean) {
+        Box(Modifier.testTag(testTag).padding(20.dp), contentAlignment = Alignment.Center) {
+            DropdownMenuContent(
+                expandedStates = MutableTransitionState(initialState = true),
+                transformOriginState = mutableStateOf(TransformOrigin.Center)
+            ) {
+                DropdownMenuItem(
+                    text = { Text("Edit") },
+                    onClick = { },
+                    enabled = enabledItems,
+                    leadingIcon = {
+                        Icon(
+                            Icons.Outlined.Edit,
+                            contentDescription = null
+                        )
+                    })
+                DropdownMenuItem(
+                    text = { Text("Settings") },
+                    onClick = { },
+                    enabled = enabledItems,
+                    leadingIcon = {
+                        Icon(
+                            Icons.Outlined.Settings,
+                            contentDescription = null
+                        )
+                    },
+                    trailingIcon = { Text("F11", textAlign = TextAlign.Center) })
+                Divider()
+                DropdownMenuItem(
+                    text = { Text("Send Feedback") },
+                    onClick = { },
+                    enabled = enabledItems,
+                    leadingIcon = {
+                        Icon(
+                            Icons.Outlined.Email,
+                            contentDescription = null
+                        )
+                    },
+                    trailingIcon = {
+                        Icon(
+                            Icons.Outlined.Lock,
+                            contentDescription = null
+                        )
+                    })
+            }
+        }
+    }
+
+    private fun assertAgainstGolden(goldenIdentifier: String) {
+        composeTestRule.onNodeWithTag(testTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, goldenIdentifier)
+    }
+}
\ No newline at end of file
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt
new file mode 100644
index 0000000..124eac9
--- /dev/null
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt
@@ -0,0 +1,362 @@
+/*
+ * Copyright 2022 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
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.hasAnyDescendant
+import androidx.compose.ui.test.hasTestTag
+import androidx.compose.ui.test.isPopup
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.DpOffset
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalTestApi::class)
+class MenuTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun menu_canBeTriggered() {
+        var expanded by mutableStateOf(false)
+
+        rule.setContent {
+            Box(Modifier.requiredSize(20.dp).background(color = Color.Blue)) {
+                DropdownMenu(
+                    expanded = expanded,
+                    onDismissRequest = {}
+                ) {
+                    DropdownMenuItem(
+                        text = { Text("Option 1") },
+                        modifier = Modifier.testTag("MenuContent"),
+                        onClick = {})
+                }
+            }
+        }
+
+        rule.onNodeWithTag("MenuContent").assertDoesNotExist()
+        rule.mainClock.autoAdvance = false
+
+        rule.runOnUiThread { expanded = true }
+        rule.mainClock.advanceTimeByFrame() // Trigger the popup
+        rule.waitForIdle()
+        rule.mainClock.advanceTimeByFrame() // Kick off the animation
+        rule.mainClock.advanceTimeBy(InTransitionDuration.toLong())
+        rule.onNodeWithTag("MenuContent").assertExists()
+
+        rule.runOnUiThread { expanded = false }
+        rule.mainClock.advanceTimeByFrame() // Trigger the popup
+        rule.mainClock.advanceTimeByFrame() // Kick off the animation
+        rule.mainClock.advanceTimeBy(OutTransitionDuration.toLong())
+        rule.mainClock.advanceTimeByFrame()
+        rule.onNodeWithTag("MenuContent").assertDoesNotExist()
+
+        rule.runOnUiThread { expanded = true }
+        rule.mainClock.advanceTimeByFrame() // Trigger the popup
+        rule.waitForIdle()
+        rule.mainClock.advanceTimeByFrame() // Kick off the animation
+        rule.mainClock.advanceTimeBy(InTransitionDuration.toLong())
+        rule.onNodeWithTag("MenuContent").assertExists()
+    }
+
+    @Test
+    fun menu_hasExpectedSize() {
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.requiredSize(20.toDp()).background(color = Color.Blue)) {
+                    DropdownMenu(
+                        expanded = true,
+                        onDismissRequest = {}
+                    ) {
+                        Box(Modifier.testTag("MenuContent1").size(70.toDp()))
+                        Box(Modifier.testTag("MenuContent2").size(130.toDp()))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("MenuContent1").assertExists()
+        rule.onNodeWithTag("MenuContent2").assertExists()
+        val node = rule.onNode(
+            isPopup() and hasAnyDescendant(hasTestTag("MenuContent1")) and
+                hasAnyDescendant(hasTestTag("MenuContent2"))
+        ).assertExists().fetchSemanticsNode()
+        with(rule.density) {
+            assertThat(node.size.width).isEqualTo(130)
+            assertThat(node.size.height)
+                .isEqualTo(DropdownMenuVerticalPadding.roundToPx() * 2 + 200)
+        }
+    }
+
+    @Test
+    fun menu_positioning_bottomEnd() {
+        val screenWidth = 500
+        val screenHeight = 1000
+        val density = Density(1f)
+        val windowSize = IntSize(screenWidth, screenHeight)
+        val anchorPosition = IntOffset(100, 200)
+        val anchorSize = IntSize(10, 20)
+        val offsetX = 20
+        val offsetY = 40
+        val popupSize = IntSize(50, 80)
+
+        val ltrPosition = DropdownMenuPositionProvider(
+            DpOffset(offsetX.dp, offsetY.dp),
+            density
+        ).calculatePosition(
+            IntRect(anchorPosition, anchorSize),
+            windowSize,
+            LayoutDirection.Ltr,
+            popupSize
+        )
+
+        assertThat(ltrPosition.x).isEqualTo(
+            anchorPosition.x + offsetX
+        )
+        assertThat(ltrPosition.y).isEqualTo(
+            anchorPosition.y + anchorSize.height + offsetY
+        )
+
+        val rtlPosition = DropdownMenuPositionProvider(
+            DpOffset(offsetX.dp, offsetY.dp),
+            density
+        ).calculatePosition(
+            IntRect(anchorPosition, anchorSize),
+            windowSize,
+            LayoutDirection.Rtl,
+            popupSize
+        )
+
+        assertThat(rtlPosition.x).isEqualTo(
+            anchorPosition.x + anchorSize.width - offsetX - popupSize.width
+        )
+        assertThat(rtlPosition.y).isEqualTo(
+            anchorPosition.y + anchorSize.height + offsetY
+        )
+    }
+
+    @Test
+    fun menu_positioning_topStart() {
+        val screenWidth = 500
+        val screenHeight = 1000
+        val density = Density(1f)
+        val windowSize = IntSize(screenWidth, screenHeight)
+        val anchorPosition = IntOffset(450, 950)
+        val anchorPositionRtl = IntOffset(50, 950)
+        val anchorSize = IntSize(10, 20)
+        val offsetX = 20
+        val offsetY = 40
+        val popupSize = IntSize(150, 80)
+
+        val ltrPosition = DropdownMenuPositionProvider(
+            DpOffset(offsetX.dp, offsetY.dp),
+            density
+        ).calculatePosition(
+            IntRect(anchorPosition, anchorSize),
+            windowSize,
+            LayoutDirection.Ltr,
+            popupSize
+        )
+
+        assertThat(ltrPosition.x).isEqualTo(
+            anchorPosition.x + anchorSize.width - offsetX - popupSize.width
+        )
+        assertThat(ltrPosition.y).isEqualTo(
+            anchorPosition.y - popupSize.height - offsetY
+        )
+
+        val rtlPosition = DropdownMenuPositionProvider(
+            DpOffset(offsetX.dp, offsetY.dp),
+            density
+        ).calculatePosition(
+            IntRect(anchorPositionRtl, anchorSize),
+            windowSize,
+            LayoutDirection.Rtl,
+            popupSize
+        )
+
+        assertThat(rtlPosition.x).isEqualTo(
+            anchorPositionRtl.x + offsetX
+        )
+        assertThat(rtlPosition.y).isEqualTo(
+            anchorPositionRtl.y - popupSize.height - offsetY
+        )
+    }
+
+    @Test
+    fun menu_positioning_top() {
+        val screenWidth = 500
+        val screenHeight = 1000
+        val density = Density(1f)
+        val windowSize = IntSize(screenWidth, screenHeight)
+        val anchorPosition = IntOffset(0, 0)
+        val anchorSize = IntSize(50, 20)
+        val popupSize = IntSize(150, 500)
+
+        // The min margin above and below the menu, relative to the screen.
+        val menuVerticalMargin = 48.dp
+        val verticalMargin = with(density) { menuVerticalMargin.roundToPx() }
+
+        val position = DropdownMenuPositionProvider(
+            DpOffset(0.dp, 0.dp),
+            density
+        ).calculatePosition(
+            IntRect(anchorPosition, anchorSize),
+            windowSize,
+            LayoutDirection.Ltr,
+            popupSize
+        )
+
+        assertThat(position.y).isEqualTo(
+            verticalMargin
+        )
+    }
+
+    @Test
+    fun menu_positioning_anchorPartiallyVisible() {
+        val screenWidth = 500
+        val screenHeight = 1000
+        val density = Density(1f)
+        val windowSize = IntSize(screenWidth, screenHeight)
+        val anchorPosition = IntOffset(-25, -10)
+        val anchorPositionRtl = IntOffset(525, -10)
+        val anchorSize = IntSize(50, 20)
+        val popupSize = IntSize(150, 500)
+
+        // The min margin above and below the menu, relative to the screen.
+        val menuVerticalMargin = 48.dp
+        val verticalMargin = with(density) { menuVerticalMargin.roundToPx() }
+
+        val position = DropdownMenuPositionProvider(
+            DpOffset(0.dp, 0.dp),
+            density
+        ).calculatePosition(
+            IntRect(anchorPosition, anchorSize),
+            windowSize,
+            LayoutDirection.Ltr,
+            popupSize
+        )
+
+        assertThat(position.x).isEqualTo(
+            0
+        )
+        assertThat(position.y).isEqualTo(
+            verticalMargin
+        )
+
+        val rtlPosition = DropdownMenuPositionProvider(
+            DpOffset(0.dp, 0.dp),
+            density
+        ).calculatePosition(
+            IntRect(anchorPositionRtl, anchorSize),
+            windowSize,
+            LayoutDirection.Rtl,
+            popupSize
+        )
+
+        assertThat(rtlPosition.x).isEqualTo(
+            screenWidth - popupSize.width
+        )
+        assertThat(rtlPosition.y).isEqualTo(
+            verticalMargin
+        )
+    }
+
+    @Test
+    fun menu_positioning_callback() {
+        val screenWidth = 500
+        val screenHeight = 1000
+        val density = Density(1f)
+        val windowSize = IntSize(screenWidth, screenHeight)
+        val anchorPosition = IntOffset(100, 200)
+        val anchorSize = IntSize(10, 20)
+        val offsetX = 20
+        val offsetY = 40
+        val popupSize = IntSize(50, 80)
+
+        var obtainedParentBounds = IntRect(0, 0, 0, 0)
+        var obtainedMenuBounds = IntRect(0, 0, 0, 0)
+        DropdownMenuPositionProvider(
+            DpOffset(offsetX.dp, offsetY.dp),
+            density
+        ) { parentBounds, menuBounds ->
+            obtainedParentBounds = parentBounds
+            obtainedMenuBounds = menuBounds
+        }.calculatePosition(
+            IntRect(anchorPosition, anchorSize),
+            windowSize,
+            LayoutDirection.Ltr,
+            popupSize
+        )
+
+        assertThat(obtainedParentBounds).isEqualTo(IntRect(anchorPosition, anchorSize))
+        assertThat(obtainedMenuBounds).isEqualTo(
+            IntRect(
+                anchorPosition.x + offsetX,
+                anchorPosition.y + anchorSize.height + offsetY,
+                anchorPosition.x + offsetX + popupSize.width,
+                anchorPosition.y + anchorSize.height + offsetY + popupSize.height
+            )
+        )
+    }
+
+    @Test
+    fun dropdownMenuItem_onClick() {
+        var clicked = false
+        val onClick: () -> Unit = { clicked = true }
+
+        rule.setContent {
+            DropdownMenuItem(
+                text = { Box(Modifier.requiredSize(40.dp)) },
+                onClick,
+                modifier = Modifier.testTag("MenuItem").clickable(onClick = onClick),
+            )
+        }
+
+        rule.onNodeWithTag("MenuItem").performClick()
+
+        rule.runOnIdle {
+            assertThat(clicked).isTrue()
+        }
+    }
+}
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SurfaceTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SurfaceTest.kt
index 0d52061..265b771 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SurfaceTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SurfaceTest.kt
@@ -307,28 +307,7 @@
     }
 
     @Test
-    fun clickableOverload_clickAction() {
-        val count = mutableStateOf(0f)
-        rule.setMaterialContent(lightColorScheme()) {
-            Surface(
-                modifier = Modifier.testTag("surface"),
-                onClick = { count.value += 1 }
-            ) {
-                Spacer(Modifier.size(30.dp))
-            }
-        }
-        rule.onNodeWithTag("surface")
-            .performClick()
-        Truth.assertThat(count.value).isEqualTo(1)
-
-        rule.onNodeWithTag("surface")
-            .performClick()
-            .performClick()
-        Truth.assertThat(count.value).isEqualTo(3)
-    }
-
-    @Test
-    fun clickable_clickActionWithModifier() {
+    fun clickable_clickAction() {
         val count = mutableStateOf(0f)
         val interactionSource = MutableInteractionSource()
         rule.setMaterialContent(lightColorScheme()) {
@@ -382,7 +361,7 @@
     }
 
     @Test
-    fun clickableOverload_interactionSource() {
+    fun clickable_interactionSource() {
         val interactionSource = MutableInteractionSource()
 
         var scope: CoroutineScope? = null
@@ -392,8 +371,12 @@
         rule.setContent {
             scope = rememberCoroutineScope()
             Surface(
-                modifier = Modifier.testTag("surface"),
-                onClick = {},
+                modifier =
+                Modifier.testTag("surface")
+                    .clickable(
+                        interactionSource = interactionSource,
+                        indication = null,
+                        onClick = {}),
                 interactionSource = interactionSource
             ) {
                 Spacer(Modifier.size(30.dp))
@@ -433,7 +416,24 @@
         }
     }
 
-    // TODO(b/198216553): Add surface_blockClicksBehind test from M2 after Button is added.
+    @Test
+    fun surface_blockClicksBehind() {
+        val state = mutableStateOf(0)
+        rule.setContent {
+            Box(Modifier.fillMaxSize()) {
+                Button(
+                    modifier = Modifier.fillMaxSize().testTag("clickable"),
+                    onClick = { state.value += 1 }
+                ) { Text("button fullscreen") }
+                Surface(
+                    Modifier.fillMaxSize().testTag("surface"),
+                ) {}
+            }
+        }
+        rule.onNodeWithTag("clickable").assertHasClickAction().performClick()
+        // still 0
+        Truth.assertThat(state.value).isEqualTo(0)
+    }
 
     // regression test for b/189411183
     @Test
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabScreenshotTest.kt
new file mode 100644
index 0000000..1b6c68f
--- /dev/null
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabScreenshotTest.kt
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2022 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
+
+import android.os.Build
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.PressInteraction
+import androidx.compose.foundation.layout.Box
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+@OptIn(ExperimentalTestApi::class)
+class TabScreenshotTest {
+
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL3)
+
+    @Test
+    fun lightTheme() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(lightColorScheme()) {
+                DefaultTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "tabs_lightTheme"
+        )
+    }
+
+    @Test
+    fun lightTheme_pressed() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(lightColorScheme()) {
+                DefaultTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = PressInteraction.Press(Offset(10f, 10f)),
+            goldenIdentifier = "tabs_lightTheme_pressed"
+        )
+    }
+
+    @Test
+    fun darkTheme() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                DefaultTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "tabs_darkTheme"
+        )
+    }
+
+    @Test
+    fun darkTheme_pressed() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                DefaultTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = PressInteraction.Press(Offset(10f, 10f)),
+            goldenIdentifier = "tabs_darkTheme_pressed"
+        )
+    }
+
+    @Test
+    fun leadingIconTabs_lightTheme() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(lightColorScheme()) {
+                DefaultLeadingIconTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "leadingIconTabs_lightTheme"
+        )
+    }
+
+    @Test
+    fun leadingIconTabs_darkTheme() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                DefaultLeadingIconTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "leadingIconTabs_darkTheme"
+        )
+    }
+
+    /**
+     * Asserts that the tabs match the screenshot with identifier [goldenIdentifier].
+     *
+     * @param interactionSource the [MutableInteractionSource] used for the first Tab
+     * @param interaction the [Interaction] to assert for, or `null` if no [Interaction].
+     * @param goldenIdentifier the identifier for the corresponding screenshot
+     */
+    private fun assertTabsMatch(
+        scope: CoroutineScope,
+        interactionSource: MutableInteractionSource,
+        interaction: Interaction? = null,
+        goldenIdentifier: String
+    ) {
+        if (interaction != null) {
+            composeTestRule.runOnIdle {
+                // Start ripple
+                scope.launch {
+                    interactionSource.emit(interaction)
+                }
+            }
+
+            composeTestRule.waitForIdle()
+            // Ripples are drawn on the RenderThread, not the main (UI) thread, so we can't
+            // properly wait for synchronization. Instead just wait until after the ripples are
+            // finished animating.
+            Thread.sleep(300)
+        }
+
+        // Capture and compare screenshots
+        composeTestRule.onNodeWithTag(Tag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, goldenIdentifier)
+    }
+}
+
+/**
+ * Default colored [TabRow] with three [Tab]s. The first [Tab] is selected, and the rest are not.
+ *
+ * @param interactionSource the [MutableInteractionSource] for the first [Tab], to control its
+ * visual state.
+ */
+@Composable
+private fun DefaultTabs(
+    interactionSource: MutableInteractionSource
+) {
+    Box(Modifier.semantics(mergeDescendants = true) {}.testTag(Tag)) {
+        TabRow(selectedTabIndex = 0) {
+            Tab(
+                text = { Text("TAB") },
+                selected = true,
+                interactionSource = interactionSource,
+                onClick = {}
+            )
+            Tab(
+                text = { Text("TAB") },
+                selected = false,
+                onClick = {}
+            )
+            Tab(
+                text = { Text("TAB") },
+                selected = false,
+                onClick = {}
+            )
+        }
+    }
+}
+
+/**
+ * Custom colored [TabRow] with three [Tab]s. The first [Tab] is selected, and the rest are not.
+ *
+ * @param interactionSource the [MutableInteractionSource] for the first [Tab], to control its
+ * visual state.
+ * @param containerColor the containerColor of the [TabRow]
+ * @param selectedContentColor the content color for a selected [Tab] (first tab)
+ * @param unselectedContentColor the content color for an unselected [Tab] (second and third tabs)
+ */
+@Composable
+private fun CustomTabs(
+    interactionSource: MutableInteractionSource,
+    containerColor: Color,
+    selectedContentColor: Color,
+    unselectedContentColor: Color
+) {
+    Box(Modifier.semantics(mergeDescendants = true) {}.testTag(Tag)) {
+        TabRow(selectedTabIndex = 0,
+            containerColor = containerColor,
+            indicator = @Composable { tabPositions ->
+                TabRowDefaults.Indicator(
+                    modifier = Modifier.tabIndicatorOffset(tabPositions[0]),
+                    color = selectedContentColor
+                )
+            }) {
+            Tab(
+                text = { Text("TAB") },
+                selected = true,
+                interactionSource = interactionSource,
+                selectedContentColor = selectedContentColor,
+                unselectedContentColor = unselectedContentColor,
+                onClick = {}
+            )
+            Tab(
+                text = { Text("TAB") },
+                selected = false,
+                selectedContentColor = selectedContentColor,
+                unselectedContentColor = unselectedContentColor,
+                onClick = {}
+            )
+            Tab(
+                text = { Text("TAB") },
+                selected = false,
+                selectedContentColor = selectedContentColor,
+                unselectedContentColor = unselectedContentColor,
+                onClick = {}
+            )
+        }
+    }
+}
+
+/**
+ * Default colored [TabRow] with three [LeadingIconTab]s. The first [LeadingIconTab] is selected,
+ * and the rest are not.
+ *
+ * @param interactionSource the [MutableInteractionSource] for the first [LeadingIconTab], to control its
+ * visual state.
+ */
+@Composable
+private fun DefaultLeadingIconTabs(
+    interactionSource: MutableInteractionSource
+) {
+    Box(Modifier.semantics(mergeDescendants = true) {}.testTag(Tag)) {
+        TabRow(selectedTabIndex = 0) {
+            LeadingIconTab(
+                text = { Text("TAB") },
+                icon = { Icon(Icons.Filled.Favorite, contentDescription = "Favorite") },
+                selected = true,
+                interactionSource = interactionSource,
+                onClick = {}
+            )
+            LeadingIconTab(
+                text = { Text("TAB") },
+                icon = { Icon(Icons.Filled.Favorite, contentDescription = "Favorite") },
+                selected = false,
+                onClick = {}
+            )
+            LeadingIconTab(
+                text = { Text("TAB") },
+                icon = { Icon(Icons.Filled.Favorite, contentDescription = "Favorite") },
+                selected = false,
+                onClick = {}
+            )
+        }
+    }
+}
+
+private const val Tag = "Tab"
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt
new file mode 100644
index 0000000..b64fdee
--- /dev/null
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt
@@ -0,0 +1,841 @@
+/*
+ * Copyright 2022 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
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material3.samples.LeadingIconTabs
+import androidx.compose.material3.samples.ScrollingTextTabs
+import androidx.compose.material3.samples.TextTabs
+import androidx.compose.material3.tokens.PrimaryNavigationTabTokens
+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.testutils.assertIsEqualTo
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertCountEquals
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsNotSelected
+import androidx.compose.ui.test.assertIsSelected
+import androidx.compose.ui.test.assertPositionInRootIsEqualTo
+import androidx.compose.ui.test.getBoundsInRoot
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
+import androidx.compose.ui.test.isSelectable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onParent
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.height
+import androidx.compose.ui.unit.width
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class TabTest {
+
+    private val ExpectedSmallTabHeight = 48.dp
+    private val ExpectedLargeTabHeight = 72.dp
+
+    private val icon = Icons.Filled.Favorite
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @Test
+    fun defaultSemantics() {
+        rule.setMaterialContent(lightColorScheme()) {
+            TabRow(0) {
+                Tab(
+                    text = { Text("Text") },
+                    modifier = Modifier.testTag("tab"),
+                    selected = true,
+                    onClick = {}
+                )
+            }
+        }
+
+        rule.onNodeWithTag("tab")
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Tab))
+            .assertIsSelected()
+            .assertIsEnabled()
+            .assertHasClickAction()
+
+        rule.onNodeWithTag("tab")
+            .onParent()
+            .assert(SemanticsMatcher.keyIsDefined(SemanticsProperties.SelectableGroup))
+    }
+
+    @Test
+    fun disabledSemantics() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                Tab(
+                    enabled = false,
+                    text = { Text("Text") },
+                    modifier = Modifier.testTag("tab"),
+                    selected = true,
+                    onClick = {}
+                )
+            }
+        }
+
+        rule.onNodeWithTag("tab")
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Tab))
+            .assertIsSelected()
+            .assertIsNotEnabled()
+            .assertHasClickAction()
+    }
+
+    @Test
+    fun leadingIconTab_defaultSemantics() {
+        rule.setMaterialContent(lightColorScheme()) {
+            TabRow(0) {
+                LeadingIconTab(
+                    text = { Text("Text") },
+                    icon = { Icon(icon, null) },
+                    modifier = Modifier.testTag("leadingIconTab"),
+                    selected = true,
+                    onClick = {}
+                )
+            }
+        }
+
+        rule.onNodeWithTag("leadingIconTab")
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Tab))
+            .assertIsSelected()
+            .assertIsEnabled()
+            .assertHasClickAction()
+
+        rule.onNodeWithTag("leadingIconTab")
+            .onParent()
+            .assert(SemanticsMatcher.keyIsDefined(SemanticsProperties.SelectableGroup))
+    }
+
+    @Test
+    fun leadingIconTab_disabledSemantics() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                LeadingIconTab(
+                    enabled = false,
+                    text = { Text("Text") },
+                    icon = { Icon(icon, null) },
+                    modifier = Modifier.testTag("leadingIconTab"),
+                    selected = true,
+                    onClick = {}
+                )
+            }
+        }
+
+        rule.onNodeWithTag("leadingIconTab")
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Tab))
+            .assertIsSelected()
+            .assertIsNotEnabled()
+            .assertHasClickAction()
+    }
+
+    @Test
+    fun textTab_height() {
+        rule
+            .setMaterialContentForSizeAssertions {
+                Tab(text = { Text("Text") }, selected = true, onClick = {})
+            }
+            .assertHeightIsEqualTo(ExpectedSmallTabHeight)
+    }
+
+    @Test
+    fun iconTab_height() {
+        rule
+            .setMaterialContentForSizeAssertions {
+                Tab(icon = { Icon(icon, null) }, selected = true, onClick = {})
+            }
+            .assertHeightIsEqualTo(ExpectedSmallTabHeight)
+    }
+
+    @Test
+    fun textAndIconTab_height() {
+        rule
+            .setMaterialContentForSizeAssertions {
+                Surface {
+                    Tab(
+                        text = { Text("Text and Icon") },
+                        icon = { Icon(icon, null) },
+                        selected = true,
+                        onClick = {}
+                    )
+                }
+            }
+            .assertHeightIsEqualTo(ExpectedLargeTabHeight)
+    }
+
+    @Test
+    fun leadingIconTab_height() {
+        rule
+            .setMaterialContentForSizeAssertions {
+                Surface {
+                    LeadingIconTab(
+                        text = { Text("Text") },
+                        icon = { Icon(icon, null) },
+                        selected = true,
+                        onClick = {}
+                    )
+                }
+            }
+            .assertHeightIsEqualTo(ExpectedSmallTabHeight)
+    }
+
+    @Test
+    fun fixedTabRow_indicatorPosition() {
+        val indicatorHeight = 1.dp
+
+        rule.setMaterialContent(lightColorScheme()) {
+            var state by remember { mutableStateOf(0) }
+            val titles = listOf("TAB 1", "TAB 2")
+
+            val indicator = @Composable { tabPositions: List<TabPosition> ->
+                Box(
+                    Modifier
+                        .tabIndicatorOffset(tabPositions[state])
+                        .fillMaxWidth()
+                        .height(indicatorHeight)
+                        .background(color = Color.Red)
+                        .testTag("indicator")
+                )
+            }
+
+            Box(Modifier.testTag("tabRow")) {
+                TabRow(
+                    selectedTabIndex = state,
+                    indicator = indicator
+                ) {
+                    titles.forEachIndexed { index, title ->
+                        Tab(
+                            text = { Text(title) },
+                            selected = state == index,
+                            onClick = { state = index }
+                        )
+                    }
+                }
+            }
+        }
+
+        val tabRowBounds = rule.onNodeWithTag("tabRow").getUnclippedBoundsInRoot()
+
+        rule.onNodeWithTag("indicator", true)
+            .assertPositionInRootIsEqualTo(
+                expectedLeft = 0.dp,
+                expectedTop = tabRowBounds.height - indicatorHeight
+            )
+
+        // Click the second tab
+        rule.onAllNodes(isSelectable())[1].performClick()
+
+        // Indicator should now be placed in the bottom left of the second tab, so its x coordinate
+        // should be in the middle of the TabRow
+        rule.onNodeWithTag("indicator", true)
+            .assertPositionInRootIsEqualTo(
+                expectedLeft = (tabRowBounds.width / 2),
+                expectedTop = tabRowBounds.height - indicatorHeight
+            )
+    }
+
+    @Test
+    fun fixedTabRow_dividerHeight() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val titles = listOf("TAB 1", "TAB 2")
+            val tabRowHeight = 100.dp
+
+            val divider = @Composable { TabRowDefaults.Divider(Modifier.testTag("divider")) }
+
+            Box(Modifier.testTag("tabRow")) {
+                TabRow(
+                    modifier = Modifier.height(tabRowHeight),
+                    selectedTabIndex = 0,
+                    divider = divider
+                ) {
+                    titles.forEachIndexed { index, title ->
+                        Tab(
+                            modifier = Modifier.height(tabRowHeight),
+                            text = { Text(title) },
+                            selected = index == 0,
+                            onClick = {}
+                        )
+                    }
+                }
+            }
+        }
+
+        val tabRowBounds = rule.onNodeWithTag("tabRow").getBoundsInRoot()
+
+        rule.onNodeWithTag("divider", true)
+            .assertPositionInRootIsEqualTo(
+                expectedLeft = 0.dp,
+                expectedTop = tabRowBounds.height - PrimaryNavigationTabTokens.DividerHeight
+            )
+            .assertHeightIsEqualTo(PrimaryNavigationTabTokens.DividerHeight)
+    }
+
+    @Test
+    fun singleLineTab_textPosition() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var state by remember { mutableStateOf(0) }
+            val titles = listOf("TAB")
+
+            Box {
+                TabRow(
+                    modifier = Modifier.testTag("tabRow"),
+                    selectedTabIndex = state
+                ) {
+                    titles.forEachIndexed { index, title ->
+                        Tab(
+                            text = {
+                                Text(title, Modifier.testTag("text"))
+                            },
+                            selected = state == index,
+                            onClick = { state = index }
+                        )
+                    }
+                }
+            }
+        }
+
+        val tabRowBounds = rule.onNodeWithTag("tabRow").getUnclippedBoundsInRoot()
+        val textBounds =
+            rule.onNodeWithTag("text", useUnmergedTree = true).getUnclippedBoundsInRoot()
+        val expectedPositionY = (tabRowBounds.height - textBounds.height) / 2
+        textBounds.top.assertIsEqualTo(expectedPositionY, "text bounds top y-position")
+    }
+
+    @Test
+    fun singleLineTab_withIcon_textBaseline() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var state by remember { mutableStateOf(0) }
+            val titles = listOf("TAB")
+
+            Box {
+                TabRow(
+                    modifier = Modifier.testTag("tabRow"),
+                    selectedTabIndex = state
+                ) {
+                    titles.forEachIndexed { index, title ->
+                        Tab(
+                            text = {
+                                Text(title, Modifier.testTag("text"))
+                            },
+                            icon = { Icon(Icons.Filled.Favorite, null) },
+                            selected = state == index,
+                            onClick = { state = index }
+                        )
+                    }
+                }
+            }
+        }
+
+        val expectedBaseline = 14.dp
+        val indicatorHeight = 3.dp
+        val expectedBaselineDistance = expectedBaseline + indicatorHeight
+
+        val tabRowBounds = rule.onNodeWithTag("tabRow").getUnclippedBoundsInRoot()
+        val textBounds =
+            rule.onNodeWithTag("text", useUnmergedTree = true).getUnclippedBoundsInRoot()
+        val textBaselinePos =
+            rule.onNodeWithTag("text", useUnmergedTree = true).getLastBaselinePosition()
+
+        val baselinePositionY = textBounds.top + textBaselinePos
+        val expectedPositionY = tabRowBounds.height - expectedBaselineDistance
+        baselinePositionY.assertIsEqualTo(expectedPositionY, "baseline y-position")
+    }
+
+    @Test
+    fun twoLineTab_textPosition() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var state by remember { mutableStateOf(0) }
+            val titles = listOf("Two line \n text")
+
+            Box {
+                TabRow(
+                    modifier = Modifier.testTag("tabRow"),
+                    selectedTabIndex = state
+                ) {
+                    titles.forEachIndexed { index, title ->
+                        Tab(
+                            text = {
+                                Text(title, Modifier.testTag("text"), maxLines = 2)
+                            },
+                            selected = state == index,
+                            onClick = { state = index }
+                        )
+                    }
+                }
+            }
+        }
+
+        val tabRowBounds = rule.onNodeWithTag("tabRow").getUnclippedBoundsInRoot()
+        val textBounds =
+            rule.onNodeWithTag("text", useUnmergedTree = true).getUnclippedBoundsInRoot()
+
+        val expectedPositionY = (tabRowBounds.height - textBounds.height) / 2
+        textBounds.top.assertIsEqualTo(expectedPositionY, "text bounds top y-position")
+    }
+
+    @Test
+    fun LeadingIconTab_textAndIconPosition() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                TabRow(
+                    modifier = Modifier.testTag("tabRow"),
+                    selectedTabIndex = 0
+                ) {
+                    LeadingIconTab(
+                        text = {
+                            Text("TAB", Modifier.testTag("text"))
+                        },
+                        icon = { Icon(Icons.Filled.Favorite, null, Modifier.testTag("icon")) },
+                        selected = true,
+                        onClick = {}
+                    )
+                }
+            }
+        }
+
+        val tabRowBounds =
+            rule.onNodeWithTag("tabRow", useUnmergedTree = true).getUnclippedBoundsInRoot()
+
+        val textBounds =
+            rule.onNodeWithTag("text", useUnmergedTree = true).getUnclippedBoundsInRoot()
+
+        val textDistanceFromIcon = 8.dp
+
+        val iconBounds =
+            rule.onNodeWithTag("icon", useUnmergedTree = true).getUnclippedBoundsInRoot()
+        textBounds.left.assertIsEqualTo(
+            iconBounds.right + textDistanceFromIcon,
+            "textBounds left-position"
+        )
+
+        val iconOffset =
+            (tabRowBounds.width - iconBounds.width - textBounds.width - textDistanceFromIcon) / 2
+        iconBounds.left.assertIsEqualTo(iconOffset, "iconBounds left-position")
+    }
+
+    @Test
+    fun scrollableTabRow_indicatorPosition() {
+        val indicatorHeight = 1.dp
+        val minimumTabWidth = 90.dp
+
+        rule.setMaterialContent(lightColorScheme()) {
+            var state by remember { mutableStateOf(0) }
+            val titles = listOf("TAB 1", "TAB 2")
+
+            val indicator = @Composable { tabPositions: List<TabPosition> ->
+                Box(
+                    Modifier
+                        .tabIndicatorOffset(tabPositions[state])
+                        .fillMaxWidth()
+                        .height(indicatorHeight)
+                        .background(color = Color.Red)
+                        .testTag("indicator")
+                )
+            }
+
+            Box {
+                ScrollableTabRow(
+                    modifier = Modifier.testTag("tabRow"),
+                    selectedTabIndex = state,
+                    indicator = indicator
+                ) {
+                    titles.forEachIndexed { index, title ->
+                        Tab(
+                            text = { Text(title) },
+                            selected = state == index,
+                            onClick = { state = index }
+                        )
+                    }
+                }
+            }
+        }
+
+        val tabRowBounds = rule.onNodeWithTag("tabRow").getUnclippedBoundsInRoot()
+        val tabRowPadding = 52.dp
+        // Indicator should be placed in the bottom left of the first tab
+        rule.onNodeWithTag("indicator", true)
+            .assertPositionInRootIsEqualTo(
+                // Tabs in a scrollable tab row are offset 52.dp from each end
+                expectedLeft = tabRowPadding,
+                expectedTop = tabRowBounds.height - indicatorHeight
+            )
+
+        // Click the second tab
+        rule.onAllNodes(isSelectable())[1].performClick()
+
+        // Indicator should now be placed in the bottom left of the second tab, so its x coordinate
+        // should be in the middle of the TabRow
+        rule.onNodeWithTag("indicator", true)
+            .assertPositionInRootIsEqualTo(
+                expectedLeft = tabRowPadding + minimumTabWidth,
+                expectedTop = tabRowBounds.height - indicatorHeight
+            )
+    }
+
+    @Test
+    fun scrollableTabRow_dividerHeight() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val titles = listOf("TAB 1", "TAB 2")
+            val tabRowHeight = 100.dp
+
+            val divider = @Composable { TabRowDefaults.Divider(Modifier.testTag("divider")) }
+
+            Box(Modifier.testTag("tabRow")) {
+                ScrollableTabRow(
+                    modifier = Modifier.height(tabRowHeight),
+                    selectedTabIndex = 0,
+                    divider = divider
+                ) {
+                    titles.forEachIndexed { index, title ->
+                        Tab(
+                            modifier = Modifier.height(tabRowHeight),
+                            text = { Text(title) },
+                            selected = index == 0,
+                            onClick = {}
+                        )
+                    }
+                }
+            }
+        }
+
+        val tabRowBounds = rule.onNodeWithTag("tabRow").getBoundsInRoot()
+
+        rule.onNodeWithTag("divider", true)
+            .assertPositionInRootIsEqualTo(
+                expectedLeft = 0.dp,
+                expectedTop = tabRowBounds.height - PrimaryNavigationTabTokens.DividerHeight,
+            )
+            .assertHeightIsEqualTo(PrimaryNavigationTabTokens.DividerHeight)
+    }
+
+    @Test
+    fun fixedTabRow_initialTabSelected() {
+        rule
+            .setMaterialContent(lightColorScheme()) {
+                TextTabs()
+            }
+
+        // Only the first tab should be selected
+        rule.onAllNodes(isSelectable())
+            .assertCountEquals(3)
+            .apply {
+                get(0).assertIsSelected()
+                get(1).assertIsNotSelected()
+                get(2).assertIsNotSelected()
+            }
+    }
+
+    @Test
+    fun fixedTabRow_selectNewTab() {
+        rule
+            .setMaterialContent(lightColorScheme()) {
+                TextTabs()
+            }
+
+        // Only the first tab should be selected
+        rule.onAllNodes(isSelectable())
+            .assertCountEquals(3)
+            .apply {
+                get(0).assertIsSelected()
+                get(1).assertIsNotSelected()
+                get(2).assertIsNotSelected()
+            }
+
+        // Click the last tab
+        rule.onAllNodes(isSelectable())[2].performClick()
+
+        // Now only the last tab should be selected
+        rule.onAllNodes(isSelectable())
+            .assertCountEquals(3)
+            .apply {
+                get(0).assertIsNotSelected()
+                get(1).assertIsNotSelected()
+                get(2).assertIsSelected()
+            }
+    }
+
+    @Test
+    fun fixedLeadingIconTabRow_initialTabSelected() {
+        rule
+            .setMaterialContent(lightColorScheme()) {
+                LeadingIconTabs()
+            }
+
+        // Only the first tab should be selected
+        rule.onAllNodes(isSelectable())
+            .assertCountEquals(3)
+            .apply {
+                get(0).assertIsSelected()
+                get(1).assertIsNotSelected()
+                get(2).assertIsNotSelected()
+            }
+    }
+
+    @Test
+    fun LeadingIconTabRow_selectNewTab() {
+        rule
+            .setMaterialContent(lightColorScheme()) {
+                LeadingIconTabs()
+            }
+
+        // Only the first tab should be selected
+        rule.onAllNodes(isSelectable())
+            .assertCountEquals(3)
+            .apply {
+                get(0).assertIsSelected()
+                get(1).assertIsNotSelected()
+                get(2).assertIsNotSelected()
+            }
+
+        // Click the last tab
+        rule.onAllNodes(isSelectable())[2].performClick()
+
+        // Now only the last tab should be selected
+        rule.onAllNodes(isSelectable())
+            .assertCountEquals(3)
+            .apply {
+                get(0).assertIsNotSelected()
+                get(1).assertIsNotSelected()
+                get(2).assertIsSelected()
+            }
+    }
+
+    @Test
+    fun scrollableTabRow_initialTabSelected() {
+        rule
+            .setMaterialContent(lightColorScheme()) {
+                ScrollingTextTabs()
+            }
+
+        // Only the first tab should be selected
+        rule.onAllNodes(isSelectable())
+            .assertCountEquals(10)
+            .apply {
+                get(0).assertIsSelected()
+                (1..9).forEach {
+                    get(it).assertIsNotSelected()
+                }
+            }
+    }
+
+    @Test
+    fun scrollableTabRow_offScreenTabInitiallySelected() {
+        rule
+            .setMaterialContent(lightColorScheme()) {
+                var state by remember { mutableStateOf(9) }
+                val titles = List(10) { "Tab ${it + 1}" }
+                ScrollableTabRow(selectedTabIndex = state) {
+                    titles.forEachIndexed { index, title ->
+                        Tab(
+                            text = { Text(title) },
+                            selected = state == index,
+                            onClick = { state = index }
+                        )
+                    }
+                }
+            }
+
+        rule.onAllNodes(isSelectable())
+            .assertCountEquals(10)
+            .apply {
+                // The last tab should be selected and displayed (scrolled to)
+                get(9)
+                    .assertIsSelected()
+                    .assertIsDisplayed()
+            }
+    }
+
+    @Test
+    fun scrollableTabRow_selectNewTab() {
+        rule
+            .setMaterialContent(lightColorScheme()) {
+                ScrollingTextTabs()
+            }
+
+        // Only the first tab should be selected
+        rule.onAllNodes(isSelectable())
+            .assertCountEquals(10)
+            .apply {
+                get(0).assertIsSelected()
+                (1..9).forEach {
+                    get(it).assertIsNotSelected()
+                }
+            }
+
+        // Click the second tab
+        rule.onAllNodes(isSelectable())[1].performClick()
+
+        // Now only the second tab should be selected
+        rule.onAllNodes(isSelectable())
+            .assertCountEquals(10)
+            .apply {
+                get(0).assertIsNotSelected()
+                get(1).assertIsSelected()
+                (2..9).forEach {
+                    get(it).assertIsNotSelected()
+                }
+            }
+    }
+
+    @Test
+    fun tabRowIndicator_animatesWidthChange() {
+        rule.mainClock.autoAdvance = false
+
+        rule.setMaterialContent(lightColorScheme()) {
+            var state by remember { mutableStateOf(0) }
+            val titles = listOf("TAB 1", "TAB 2", "TAB 3 WITH LOTS OF TEXT")
+
+            val indicator = @Composable { tabPositions: List<TabPosition> ->
+                TabRowDefaults.Indicator(
+                    Modifier.tabIndicatorOffset(tabPositions[state])
+                        .testTag("indicator")
+                )
+            }
+
+            Box {
+                ScrollableTabRow(
+                    selectedTabIndex = state,
+                    indicator = indicator
+                ) {
+                    titles.forEachIndexed { index, title ->
+                        Tab(
+                            text = { Text(title) },
+                            selected = state == index,
+                            onClick = { state = index }
+                        )
+                    }
+                }
+            }
+        }
+
+        val initialWidth = rule.onNodeWithTag("indicator").getUnclippedBoundsInRoot().width
+
+        // Click the third tab, which is wider than the first
+        rule.onAllNodes(isSelectable())[2].performClick()
+
+        // Ensure animation starts
+        rule.mainClock.advanceTimeBy(50)
+
+        val midAnimationWidth = rule.onNodeWithTag("indicator").getUnclippedBoundsInRoot().width
+        assertThat(initialWidth).isLessThan(midAnimationWidth)
+
+        // Allow animation to complete
+        rule.mainClock.autoAdvance = true
+        rule.waitForIdle()
+
+        val finalWidth = rule.onNodeWithTag("indicator").getUnclippedBoundsInRoot().width
+        assertThat(midAnimationWidth).isLessThan(finalWidth)
+    }
+
+    @Test
+    fun testInspectorValue() {
+        val pos = TabPosition(10.0.dp, 200.0.dp)
+        rule.setContent {
+            val modifier = Modifier.tabIndicatorOffset(pos) as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("tabIndicatorOffset")
+            assertThat(modifier.valueOverride).isEqualTo(pos)
+            assertThat(modifier.inspectableElements.asIterable()).isEmpty()
+        }
+    }
+
+    @Test
+    fun disabled_noClicks() {
+        var clicks = 0
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                Tab(
+                    enabled = false,
+                    text = { Text("Text") },
+                    modifier = Modifier.testTag("tab"),
+                    selected = true,
+                    onClick = { clicks++ }
+                )
+            }
+        }
+
+        rule.onNodeWithTag("tab")
+            .performClick()
+
+        rule.runOnIdle {
+            assertThat(clicks).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun leadingIconTab_disabled_noClicks() {
+        var clicks = 0
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                LeadingIconTab(
+                    enabled = false,
+                    text = { Text("Text") },
+                    icon = { Icon(icon, null) },
+                    modifier = Modifier.testTag("tab"),
+                    selected = true,
+                    onClick = { clicks++ }
+                )
+            }
+        }
+
+        rule.onNodeWithTag("tab")
+            .performClick()
+
+        rule.runOnIdle {
+            assertThat(clicks).isEqualTo(0)
+        }
+    }
+}
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt
new file mode 100644
index 0000000..159f821
--- /dev/null
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2022 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
+
+import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.DpOffset
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Popup
+import androidx.compose.ui.window.PopupProperties
+
+/**
+ * <a href="https://m3.material.io/components/menus/overview" class="external" target="_blank">Material Design dropdown menu</a>.
+ *
+ * A dropdown menu is a compact way of displaying multiple choices. It appears upon interaction with
+ * an element (such as an icon or button) or when users perform a specific action.
+ *
+ * A [DropdownMenu] behaves similarly to a [Popup], and will use the position of the parent layout
+ * to position itself on screen. Commonly a [DropdownMenu] will be placed in a [Box] with a sibling
+ * that will be used as the 'anchor'. Note that a [DropdownMenu] by itself will not take up any
+ * space in a layout, as the menu is displayed in a separate window, on top of other content.
+ *
+ * The [content] of a [DropdownMenu] will typically be [DropdownMenuItem]s, as well as custom
+ * content. Using [DropdownMenuItem]s will result in a menu that matches the Material
+ * specification for menus. Also note that the [content] is placed inside a scrollable [Column],
+ * so using a [LazyColumn] as the root layout inside [content] is unsupported.
+ *
+ * [onDismissRequest] will be called when the menu should close - for example when there is a
+ * tap outside the menu, or when the back key is pressed.
+ *
+ * [DropdownMenu] changes its positioning depending on the available space, always trying to be
+ * fully visible. It will try to expand horizontally, depending on layout direction, to the end of
+ * its parent, then to the start of its parent, and then screen end-aligned. Vertically, it will
+ * try to expand to the bottom of its parent, then from the top of its parent, and then screen
+ * top-aligned. An [offset] can be provided to adjust the positioning of the menu for cases when
+ * the layout bounds of its parent do not coincide with its visual bounds. Note the offset will
+ * be applied in the direction in which the menu will decide to expand.
+ *
+ * Example usage:
+ * @sample androidx.compose.material3.samples.MenuSample
+ *
+ * @param expanded Whether the menu is currently open and visible to the user
+ * @param onDismissRequest Called when the user requests to dismiss the menu, such as by
+ * tapping outside the menu's bounds
+ * @param offset [DpOffset] to be added to the position of the menu
+ */
+@Suppress("ModifierParameter")
+@Composable
+fun DropdownMenu(
+    expanded: Boolean,
+    onDismissRequest: () -> Unit,
+    modifier: Modifier = Modifier,
+    offset: DpOffset = DpOffset(0.dp, 0.dp),
+    properties: PopupProperties = PopupProperties(focusable = true),
+    content: @Composable ColumnScope.() -> Unit
+) {
+    val expandedStates = remember { MutableTransitionState(false) }
+    expandedStates.targetState = expanded
+
+    if (expandedStates.currentState || expandedStates.targetState) {
+        val transformOriginState = remember { mutableStateOf(TransformOrigin.Center) }
+        val density = LocalDensity.current
+        val popupPositionProvider = DropdownMenuPositionProvider(
+            offset,
+            density
+        ) { parentBounds, menuBounds ->
+            transformOriginState.value = calculateTransformOrigin(parentBounds, menuBounds)
+        }
+
+        Popup(
+            onDismissRequest = onDismissRequest,
+            popupPositionProvider = popupPositionProvider,
+            properties = properties
+        ) {
+            DropdownMenuContent(
+                expandedStates = expandedStates,
+                transformOriginState = transformOriginState,
+                modifier = modifier,
+                content = content
+            )
+        }
+    }
+}
+
+/**
+ * <a href="https://m3.material.io/components/menus/overview" class="external" target="_blank">Material Design dropdown menu</a> item.
+ *
+ *
+ * Example usage:
+ * @sample androidx.compose.material3.samples.MenuSample
+ *
+ * @param text The menu item text
+ * @param onClick Called when the menu item was clicked
+ * @param modifier The modifier to be applied to the menu item
+ * @param leadingIcon Optional leading icon to be displayed at the beginning of the item's text
+ * @param trailingIcon Optional trailing icon to be displayed at the end of the item's text. This
+ * trailing icon slot can also accept [Text] to indicate a keyboard shortcut, for example.
+ * @param enabled Controls the enabled state of the menu item - when `false`, the menu item
+ * will not be clickable and [onClick] will not be invoked
+ * @param colors [MenuItemColors] that will be used to resolve the background and content color for
+ * this item in different states. See [MenuDefaults.itemColors].
+ * @param contentPadding the padding applied to the content of this menu item
+ * @param interactionSource the [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this DropdownMenuItem. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this DropdownMenuItem in different [Interaction]s.
+ */
+@Composable
+fun DropdownMenuItem(
+    text: @Composable () -> Unit,
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    enabled: Boolean = true,
+    colors: MenuItemColors = MenuDefaults.itemColors(),
+    contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+) {
+    DropdownMenuItemContent(
+        text = text,
+        onClick = onClick,
+        modifier = modifier,
+        leadingIcon = leadingIcon,
+        trailingIcon = trailingIcon,
+        enabled = enabled,
+        colors = colors,
+        contentPadding = contentPadding,
+        interactionSource = interactionSource,
+    )
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Button.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Button.kt
index 9613eed..c1f231a 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Button.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Button.kt
@@ -19,6 +19,7 @@
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.VectorConverter
 import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.clickable
 import androidx.compose.foundation.interaction.FocusInteraction
 import androidx.compose.foundation.interaction.HoverInteraction
 import androidx.compose.foundation.interaction.Interaction
@@ -31,10 +32,9 @@
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.defaultMinSize
 import androidx.compose.foundation.layout.padding
-import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.material3.tokens.ElevatedButtonTokens
 import androidx.compose.material3.tokens.FilledButtonTokens
-import androidx.compose.material3.tokens.FilledButtonTonalTokens
+import androidx.compose.material3.tokens.FilledTonalButtonTokens
 import androidx.compose.material3.tokens.OutlinedButtonTokens
 import androidx.compose.material3.tokens.TextButtonTokens
 import androidx.compose.material3.tokens.TypographyTokens
@@ -119,19 +119,23 @@
 
     // TODO(b/202880001): Apply shadow color from token (will not be possibly any time soon, if ever).
     Surface(
-        modifier = modifier.minimumTouchTargetSize(),
+        modifier = modifier
+            .minimumTouchTargetSize()
+            .clickable(
+                interactionSource = interactionSource,
+                indication = null,
+                enabled = enabled,
+                role = Role.Button,
+                onClick = onClick
+            ),
+        interactionSource = interactionSource,
         shape = shape,
         color = containerColor,
         contentColor = contentColor,
-        shadowElevation = shadowElevation,
         tonalElevation = tonalElevation,
-        border = border,
-        onClick = onClick,
-        enabled = enabled,
-        role = Role.Button,
-        interactionSource = interactionSource,
-        indication = rememberRipple(),
-        ) {
+        shadowElevation = shadowElevation,
+        border = border
+    ) {
         CompositionLocalProvider(LocalContentColor provides contentColor) {
             ProvideTextStyle(value = TypographyTokens.LabelLarge) {
                 Row(
@@ -266,7 +270,7 @@
     enabled: Boolean = true,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
     elevation: ButtonElevation? = ButtonDefaults.filledTonalButtonElevation(),
-    shape: Shape = FilledButtonTonalTokens.ContainerShape,
+    shape: Shape = FilledTonalButtonTokens.ContainerShape,
     border: BorderStroke? = null,
     colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(),
     contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
@@ -564,14 +568,14 @@
      */
     @Composable
     fun filledTonalButtonColors(
-        containerColor: Color = FilledButtonTonalTokens.ContainerColor.toColor(),
-        contentColor: Color = FilledButtonTonalTokens.LabelTextColor.toColor(),
-        disabledContainerColor: Color = FilledButtonTonalTokens.DisabledContainerColor
+        containerColor: Color = FilledTonalButtonTokens.ContainerColor.toColor(),
+        contentColor: Color = FilledTonalButtonTokens.LabelTextColor.toColor(),
+        disabledContainerColor: Color = FilledTonalButtonTokens.DisabledContainerColor
             .toColor()
-            .copy(alpha = FilledButtonTonalTokens.DisabledContainerOpacity),
-        disabledContentColor: Color = FilledButtonTonalTokens.DisabledLabelTextColor
+            .copy(alpha = FilledTonalButtonTokens.DisabledContainerOpacity),
+        disabledContentColor: Color = FilledTonalButtonTokens.DisabledLabelTextColor
             .toColor()
-            .copy(alpha = FilledButtonTonalTokens.DisabledLabelTextOpacity),
+            .copy(alpha = FilledTonalButtonTokens.DisabledLabelTextOpacity),
     ): ButtonColors =
         DefaultButtonColors(
             containerColor = containerColor,
@@ -716,10 +720,10 @@
      */
     @Composable
     fun filledTonalButtonElevation(
-        defaultElevation: Dp = FilledButtonTonalTokens.ContainerElevation,
-        pressedElevation: Dp = FilledButtonTonalTokens.PressedContainerElevation,
-        focusedElevation: Dp = FilledButtonTonalTokens.FocusContainerElevation,
-        hoveredElevation: Dp = FilledButtonTonalTokens.HoverContainerElevation,
+        defaultElevation: Dp = FilledTonalButtonTokens.ContainerElevation,
+        pressedElevation: Dp = FilledTonalButtonTokens.PressedContainerElevation,
+        focusedElevation: Dp = FilledTonalButtonTokens.FocusContainerElevation,
+        hoveredElevation: Dp = FilledTonalButtonTokens.HoverContainerElevation,
         disabledElevation: Dp = 0.dp
     ): ButtonElevation {
         return remember(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
new file mode 100644
index 0000000..4a357c8
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
@@ -0,0 +1,460 @@
+/*
+ * Copyright 2022 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
+
+import androidx.compose.animation.core.LinearOutSlowInEasing
+import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.material3.tokens.MenuTokens
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.DpOffset
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.PopupPositionProvider
+import kotlin.math.max
+import kotlin.math.min
+
+@Suppress("ModifierParameter")
+@Composable
+internal fun DropdownMenuContent(
+    expandedStates: MutableTransitionState<Boolean>,
+    transformOriginState: MutableState<TransformOrigin>,
+    modifier: Modifier = Modifier,
+    content: @Composable ColumnScope.() -> Unit
+) {
+    // Menu open/close animation.
+    val transition = updateTransition(expandedStates, "DropDownMenu")
+
+    val scale by transition.animateFloat(
+        transitionSpec = {
+            if (false isTransitioningTo true) {
+                // Dismissed to expanded
+                tween(
+                    durationMillis = InTransitionDuration,
+                    easing = LinearOutSlowInEasing
+                )
+            } else {
+                // Expanded to dismissed.
+                tween(
+                    durationMillis = 1,
+                    delayMillis = OutTransitionDuration - 1
+                )
+            }
+        }
+    ) {
+        if (it) {
+            // Menu is expanded.
+            1f
+        } else {
+            // Menu is dismissed.
+            0.8f
+        }
+    }
+
+    val alpha by transition.animateFloat(
+        transitionSpec = {
+            if (false isTransitioningTo true) {
+                // Dismissed to expanded
+                tween(durationMillis = 30)
+            } else {
+                // Expanded to dismissed.
+                tween(durationMillis = OutTransitionDuration)
+            }
+        }
+    ) {
+        if (it) {
+            // Menu is expanded.
+            1f
+        } else {
+            // Menu is dismissed.
+            0f
+        }
+    }
+    Surface(
+        modifier = Modifier.graphicsLayer {
+            scaleX = scale
+            scaleY = scale
+            this.alpha = alpha
+            transformOrigin = transformOriginState.value
+        },
+        shape = MenuTokens.ContainerShape,
+        color = MaterialTheme.colorScheme.fromToken(MenuTokens.ContainerColor),
+        tonalElevation = MenuTokens.ContainerElevation,
+        shadowElevation = MenuTokens.ContainerElevation
+    ) {
+        Column(
+            modifier = modifier
+                .padding(vertical = DropdownMenuVerticalPadding)
+                .width(IntrinsicSize.Max)
+                .verticalScroll(rememberScrollState()),
+            content = content
+        )
+    }
+}
+
+@Composable
+internal fun DropdownMenuItemContent(
+    text: @Composable () -> Unit,
+    onClick: () -> Unit,
+    modifier: Modifier,
+    leadingIcon: @Composable (() -> Unit)?,
+    trailingIcon: @Composable (() -> Unit)?,
+    enabled: Boolean,
+    colors: MenuItemColors,
+    contentPadding: PaddingValues,
+    interactionSource: MutableInteractionSource
+) {
+    Row(
+        modifier = modifier
+            .clickable(
+                enabled = enabled,
+                onClick = onClick,
+                interactionSource = interactionSource,
+                indication = rememberRipple(true)
+            )
+            .fillMaxWidth()
+            // Preferred min and max width used during the intrinsic measurement.
+            .sizeIn(
+                minWidth = DropdownMenuItemDefaultMinWidth,
+                maxWidth = DropdownMenuItemDefaultMaxWidth,
+                minHeight = MenuTokens.ListItemContainerHeight
+            )
+            .padding(contentPadding),
+        verticalAlignment = Alignment.CenterVertically
+    ) {
+        ProvideTextStyle(MaterialTheme.typography.fromToken(MenuTokens.ListItemLabelTextFont)) {
+            if (leadingIcon != null) {
+                CompositionLocalProvider(
+                    LocalContentColor provides colors.leadingIconColor(enabled).value,
+                ) {
+                    Box(Modifier.defaultMinSize(minWidth = MenuTokens.ListItemLeadingIconSize)) {
+                        leadingIcon()
+                    }
+                }
+            }
+            CompositionLocalProvider(LocalContentColor provides colors.textColor(enabled).value) {
+                Box(
+                    Modifier.weight(1f)
+                        .padding(
+                            start = if (leadingIcon != null) {
+                                DropdownMenuItemHorizontalPadding
+                            } else {
+                                0.dp
+                            },
+                            end = if (trailingIcon != null) {
+                                DropdownMenuItemHorizontalPadding
+                            } else {
+                                0.dp
+                            }
+                        )
+                ) {
+                    text()
+                }
+            }
+            if (trailingIcon != null) {
+                CompositionLocalProvider(
+                    LocalContentColor provides colors.trailingIconColor(enabled).value
+                ) {
+                    Box(Modifier.defaultMinSize(minWidth = MenuTokens.ListItemTrailingIconSize)) {
+                        trailingIcon()
+                    }
+                }
+            }
+        }
+    }
+}
+
+/**
+ * Contains default values used for [DropdownMenuItem].
+ */
+object MenuDefaults {
+
+    /**
+     * Creates a [MenuItemColors] that represents the default text and icon colors used in a
+     * [DropdownMenuItemContent].
+     *
+     * @param textColor the text color of this [DropdownMenuItemContent] when enabled
+     * @param leadingIconColor the leading icon color of this [DropdownMenuItemContent] when enabled
+     * @param trailingIconColor the trailing icon color of this [DropdownMenuItemContent] when
+     * enabled
+     * @param disabledTextColor the text color of this [DropdownMenuItemContent] when not enabled
+     * @param disabledLeadingIconColor the leading icon color of this [DropdownMenuItemContent] when
+     * not enabled
+     * @param disabledTrailingIconColor the trailing icon color of this [DropdownMenuItemContent]
+     * when not enabled
+     */
+    @Composable
+    fun itemColors(
+        textColor: Color = MenuTokens.ListItemLabelTextColor.toColor(),
+        leadingIconColor: Color = MenuTokens.ListItemLeadingIconColor.toColor(),
+        trailingIconColor: Color = MenuTokens.ListItemTrailingIconColor.toColor(),
+        disabledTextColor: Color =
+            MenuTokens.ListItemDisabledLabelTextColor.toColor()
+                .copy(alpha = MenuTokens.ListItemDisabledLabelTextOpacity),
+        disabledLeadingIconColor: Color = MenuTokens.ListItemDisabledLeadingIconColor.toColor()
+            .copy(alpha = MenuTokens.ListItemDisabledLeadingIconOpacity),
+        disabledTrailingIconColor: Color = MenuTokens.ListItemDisabledTrailingIconColor.toColor()
+            .copy(alpha = MenuTokens.ListItemDisabledTrailingIconOpacity),
+    ): MenuItemColors =
+        DefaultMenuItemColors(
+            textColor = textColor,
+            leadingIconColor = leadingIconColor,
+            trailingIconColor = trailingIconColor,
+            disabledTextColor = disabledTextColor,
+            disabledLeadingIconColor = disabledLeadingIconColor,
+            disabledTrailingIconColor = disabledTrailingIconColor,
+        )
+
+    /**
+     * Default padding used for [DropdownMenuItem].
+     */
+    val DropdownMenuItemContentPadding = PaddingValues(
+        horizontal = DropdownMenuItemHorizontalPadding,
+        vertical = 0.dp
+    )
+}
+
+/**
+ * Represents the text and icon colors used in a menu item at different states.
+ *
+ * - See [MenuDefaults.itemColors] for the default colors used in a [DropdownMenuItemContent].
+ */
+@Stable
+interface MenuItemColors {
+
+    /**
+     * Represents the text color for a menu item, depending on its [enabled] state.
+     *
+     * @param enabled whether the menu item is enabled
+     */
+    @Composable
+    fun textColor(enabled: Boolean): State<Color>
+
+    /**
+     * Represents the leading icon color for a menu item, depending on its [enabled] state.
+     *
+     * @param enabled whether the menu item is enabled
+     */
+    @Composable
+    fun leadingIconColor(enabled: Boolean): State<Color>
+
+    /**
+     * Represents the trailing icon color for a menu item, depending on its [enabled] state.
+     *
+     * @param enabled whether the menu item is enabled
+     */
+    @Composable
+    fun trailingIconColor(enabled: Boolean): State<Color>
+}
+
+internal fun calculateTransformOrigin(
+    parentBounds: IntRect,
+    menuBounds: IntRect
+): TransformOrigin {
+    val pivotX = when {
+        menuBounds.left >= parentBounds.right -> 0f
+        menuBounds.right <= parentBounds.left -> 1f
+        menuBounds.width == 0 -> 0f
+        else -> {
+            val intersectionCenter =
+                (
+                    max(parentBounds.left, menuBounds.left) +
+                        min(parentBounds.right, menuBounds.right)
+                    ) / 2
+            (intersectionCenter - menuBounds.left).toFloat() / menuBounds.width
+        }
+    }
+    val pivotY = when {
+        menuBounds.top >= parentBounds.bottom -> 0f
+        menuBounds.bottom <= parentBounds.top -> 1f
+        menuBounds.height == 0 -> 0f
+        else -> {
+            val intersectionCenter =
+                (
+                    max(parentBounds.top, menuBounds.top) +
+                        min(parentBounds.bottom, menuBounds.bottom)
+                    ) / 2
+            (intersectionCenter - menuBounds.top).toFloat() / menuBounds.height
+        }
+    }
+    return TransformOrigin(pivotX, pivotY)
+}
+
+// Menu positioning.
+
+/**
+ * Calculates the position of a Material [DropdownMenu].
+ */
+// TODO(popam): Investigate if this can/should consider the app window size rather than screen size
+@Immutable
+internal data class DropdownMenuPositionProvider(
+    val contentOffset: DpOffset,
+    val density: Density,
+    val onPositionCalculated: (IntRect, IntRect) -> Unit = { _, _ -> }
+) : PopupPositionProvider {
+    override fun calculatePosition(
+        anchorBounds: IntRect,
+        windowSize: IntSize,
+        layoutDirection: LayoutDirection,
+        popupContentSize: IntSize
+    ): IntOffset {
+        // The min margin above and below the menu, relative to the screen.
+        val verticalMargin = with(density) { MenuVerticalMargin.roundToPx() }
+        // The content offset specified using the dropdown offset parameter.
+        val contentOffsetX = with(density) { contentOffset.x.roundToPx() }
+        val contentOffsetY = with(density) { contentOffset.y.roundToPx() }
+
+        // Compute horizontal position.
+        val toRight = anchorBounds.left + contentOffsetX
+        val toLeft = anchorBounds.right - contentOffsetX - popupContentSize.width
+        val toDisplayRight = windowSize.width - popupContentSize.width
+        val toDisplayLeft = 0
+        val x = if (layoutDirection == LayoutDirection.Ltr) {
+            sequenceOf(
+                toRight,
+                toLeft,
+                // If the anchor gets outside of the window on the left, we want to position
+                // toDisplayLeft for proximity to the anchor. Otherwise, toDisplayRight.
+                if (anchorBounds.left >= 0) toDisplayRight else toDisplayLeft
+            )
+        } else {
+            sequenceOf(
+                toLeft,
+                toRight,
+                // If the anchor gets outside of the window on the right, we want to position
+                // toDisplayRight for proximity to the anchor. Otherwise, toDisplayLeft.
+                if (anchorBounds.right <= windowSize.width) toDisplayLeft else toDisplayRight
+            )
+        }.firstOrNull {
+            it >= 0 && it + popupContentSize.width <= windowSize.width
+        } ?: toLeft
+
+        // Compute vertical position.
+        val toBottom = maxOf(anchorBounds.bottom + contentOffsetY, verticalMargin)
+        val toTop = anchorBounds.top - contentOffsetY - popupContentSize.height
+        val toCenter = anchorBounds.top - popupContentSize.height / 2
+        val toDisplayBottom = windowSize.height - popupContentSize.height - verticalMargin
+        val y = sequenceOf(toBottom, toTop, toCenter, toDisplayBottom).firstOrNull {
+            it >= verticalMargin &&
+                it + popupContentSize.height <= windowSize.height - verticalMargin
+        } ?: toTop
+
+        onPositionCalculated(
+            anchorBounds,
+            IntRect(x, y, x + popupContentSize.width, y + popupContentSize.height)
+        )
+        return IntOffset(x, y)
+    }
+}
+
+/** Default [MenuItemColors] implementation. */
+@Immutable
+private class DefaultMenuItemColors(
+    private val textColor: Color,
+    private val leadingIconColor: Color,
+    private val trailingIconColor: Color,
+    private val disabledTextColor: Color,
+    private val disabledLeadingIconColor: Color,
+    private val disabledTrailingIconColor: Color,
+) : MenuItemColors {
+
+    @Composable
+    override fun textColor(enabled: Boolean): State<Color> {
+        return rememberUpdatedState(if (enabled) textColor else disabledTextColor)
+    }
+
+    @Composable
+    override fun leadingIconColor(enabled: Boolean): State<Color> {
+        return rememberUpdatedState(if (enabled) leadingIconColor else disabledLeadingIconColor)
+    }
+
+    @Composable
+    override fun trailingIconColor(enabled: Boolean): State<Color> {
+        return rememberUpdatedState(if (enabled) trailingIconColor else disabledTrailingIconColor)
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as DefaultMenuItemColors
+
+        if (textColor != other.textColor) return false
+        if (leadingIconColor != other.leadingIconColor) return false
+        if (trailingIconColor != other.trailingIconColor) return false
+        if (disabledTextColor != other.disabledTextColor) return false
+        if (disabledLeadingIconColor != other.disabledLeadingIconColor) return false
+        if (disabledTrailingIconColor != other.disabledTrailingIconColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = textColor.hashCode()
+        result = 31 * result + leadingIconColor.hashCode()
+        result = 31 * result + trailingIconColor.hashCode()
+        result = 31 * result + disabledTextColor.hashCode()
+        result = 31 * result + disabledLeadingIconColor.hashCode()
+        result = 31 * result + disabledTrailingIconColor.hashCode()
+        return result
+    }
+}
+
+// Size defaults.
+internal val MenuVerticalMargin = 48.dp
+private val DropdownMenuItemHorizontalPadding = 12.dp
+internal val DropdownMenuVerticalPadding = 8.dp
+private val DropdownMenuItemDefaultMinWidth = 112.dp
+private val DropdownMenuItemDefaultMaxWidth = 280.dp
+
+// Menu open/close animation.
+internal const val InTransitionDuration = 120
+internal const val OutTransitionDuration = 75
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Surface.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Surface.kt
index 5062093..86623af 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Surface.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Surface.kt
@@ -207,6 +207,10 @@
  */
 @Composable
 @NonRestartableComposable
+@Deprecated(
+    message = "Please use Surface with an InteractionSource and a Modifier.clickable() instead.",
+    level = DeprecationLevel.ERROR
+)
 fun Surface(
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tab.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tab.kt
new file mode 100644
index 0000000..0fe566f
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tab.kt
@@ -0,0 +1,435 @@
+/*
+ * Copyright 2022 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
+
+import androidx.compose.animation.animateColor
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredWidth
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.material3.tokens.PrimaryNavigationTabTokens
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.FirstBaseline
+import androidx.compose.ui.layout.LastBaseline
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+import kotlin.math.max
+
+// TODO: Provide M3 tab image when asset is available.
+/**
+ * <a href="https://material.io/components/tabs" class="external" target="_blank">Material Design tab</a>.
+ *
+ * A default Tab, also known as a Primary Navigation Tab. Tabs organize content across different
+ * screens, data sets, and other interactions.
+ *
+ * A Tab represents a single page of content using a text label and/or icon. It represents its
+ * selected state by tinting the text label and/or image with [selectedContentColor].
+ *
+ * This should typically be used inside of a [TabRow], see the corresponding documentation for
+ * example usage.
+ *
+ * This Tab has slots for [text] and/or [icon] - see the other Tab overload for a generic Tab
+ * that is not opinionated about its content.
+ *
+ * @param selected whether this tab is selected or not
+ * @param onClick the callback to be invoked when this tab is selected
+ * @param modifier optional [Modifier] for this tab
+ * @param enabled controls the enabled state of this tab. When `false`, this tab will not
+ * be clickable and will appear disabled to accessibility services.
+ * @param text the text label displayed in this tab
+ * @param icon the icon displayed in this tab
+ * @param interactionSource the [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this Tab. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this Tab in different [Interaction]s.
+ * @param selectedContentColor the color for the content of this tab when selected, and the color
+ * of the ripple.
+ * @param unselectedContentColor the color for the content of this tab when not selected
+ *
+ * @see LeadingIconTab
+ */
+@Composable
+fun Tab(
+    selected: Boolean,
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    text: @Composable (() -> Unit)? = null,
+    icon: @Composable (() -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    selectedContentColor: Color = LocalContentColor.current,
+    unselectedContentColor: Color = selectedContentColor
+) {
+    val styledText: @Composable (() -> Unit)? = text?.let {
+        @Composable {
+            val style =
+                MaterialTheme.typography.fromToken(PrimaryNavigationTabTokens.LabelTextFont)
+                .copy(textAlign = TextAlign.Center)
+            ProvideTextStyle(style, content = text)
+        }
+    }
+    Tab(
+        selected,
+        onClick,
+        modifier,
+        enabled,
+        interactionSource,
+        selectedContentColor,
+        unselectedContentColor
+    ) {
+        TabBaselineLayout(icon = icon, text = styledText)
+    }
+}
+
+// TODO: Provide M3 tab image when asset is available.
+/**
+ * <a href="https://material.io/components/tabs" class="external" target="_blank">Material Design tab</a>.
+ *
+ * Tabs organize content across different screens, data sets, and other interactions.
+ *
+ * A LeadingIconTab represents a single page of content using a text label and an icon in
+ * front of the label.
+ * It represents its selected state by tinting the text label and icon with [selectedContentColor].
+ *
+ * This should typically be used inside of a [TabRow], see the corresponding documentation for
+ * example usage.
+ *
+ * @param selected whether this tab is selected or not
+ * @param onClick the callback to be invoked when this tab is selected
+ * @param text the text label displayed in this tab
+ * @param icon the icon displayed in this tab
+ * @param modifier optional [Modifier] for this tab
+ * @param enabled controls the enabled state of this tab. When `false`, this tab will not
+ * be clickable and will appear disabled to accessibility services.
+ * @param interactionSource the [MutableInteractionSource] representing the different [Interaction]s
+ * present on this tab. You can create and pass in your own remembered [MutableInteractionSource] if
+ * you want to read the [Interaction] and customize the appearance / behavior of this tab
+ * in different [Interaction]s.
+ * @param selectedContentColor the color for the content of this tab when selected, and the color
+ * of the ripple.
+ * @param unselectedContentColor the color for the content of this tab when not selected
+ *
+ * @see Tab
+ */
+@Composable
+fun LeadingIconTab(
+    selected: Boolean,
+    onClick: () -> Unit,
+    text: @Composable (() -> Unit),
+    icon: @Composable (() -> Unit),
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    selectedContentColor: Color = LocalContentColor.current,
+    unselectedContentColor: Color = selectedContentColor
+) {
+    // The color of the Ripple should always the be selected color, as we want to show the color
+    // before the item is considered selected, and hence before the new contentColor is
+    // provided by TabTransition.
+    val ripple = rememberRipple(bounded = true, color = selectedContentColor)
+
+    TabTransition(selectedContentColor, unselectedContentColor, selected) {
+        Row(
+            modifier = modifier
+                .height(SmallTabHeight)
+                .selectable(
+                    selected = selected,
+                    onClick = onClick,
+                    enabled = enabled,
+                    role = Role.Tab,
+                    interactionSource = interactionSource,
+                    indication = ripple
+                )
+                .padding(horizontal = HorizontalTextPadding)
+                .fillMaxWidth(),
+            horizontalArrangement = Arrangement.Center,
+            verticalAlignment = Alignment.CenterVertically
+        ) {
+            icon()
+            Spacer(Modifier.requiredWidth(TextDistanceFromLeadingIcon))
+            val style = MaterialTheme.typography.fromToken(PrimaryNavigationTabTokens.LabelTextFont)
+                .copy(textAlign = TextAlign.Center)
+            ProvideTextStyle(style, content = text)
+        }
+    }
+}
+
+// TODO: Provide M3 tab image when asset is available.
+/**
+ * <a href="https://material.io/components/tabs" class="external" target="_blank">Material Design tab</a>.
+ *
+ * Tabs organize content across different screens, data sets, and other interactions.
+ *
+ * Generic [Tab] overload that is not opinionated about content / color. See the other overload
+ * for a Tab that has specific slots for text and / or an icon, as well as providing the correct
+ * colors for selected / unselected states.
+ *
+ * A custom tab using this API may look like:
+ *
+ * @sample androidx.compose.material3.samples.FancyTab
+ *
+ * @param selected whether this tab is selected or not
+ * @param onClick the callback to be invoked when this tab is selected
+ * @param modifier optional [Modifier] for this tab
+ * @param enabled controls the enabled state of this tab. When `false`, this tab will not
+ * be clickable and will appear disabled to accessibility services.
+ * @param interactionSource the [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this Tab. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this Tab in different [Interaction]s.
+ * @param selectedContentColor the color for the content of this tab when selected, and the color
+ * of the ripple.
+ * @param unselectedContentColor the color for the content of this tab when not selected
+ * @param content the content of this tab
+ */
+@Composable
+fun Tab(
+    selected: Boolean,
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    selectedContentColor: Color = LocalContentColor.current,
+    unselectedContentColor: Color = selectedContentColor,
+    content: @Composable ColumnScope.() -> Unit
+) {
+    // The color of the Ripple should always the selected color, as we want to show the color
+    // before the item is considered selected, and hence before the new contentColor is
+    // provided by TabTransition.
+    val ripple = rememberRipple(bounded = true, color = selectedContentColor)
+
+    TabTransition(selectedContentColor, unselectedContentColor, selected) {
+        Column(
+            modifier = modifier
+                .selectable(
+                    selected = selected,
+                    onClick = onClick,
+                    enabled = enabled,
+                    role = Role.Tab,
+                    interactionSource = interactionSource,
+                    indication = ripple
+                )
+                .fillMaxWidth(),
+            horizontalAlignment = Alignment.CenterHorizontally,
+            verticalArrangement = Arrangement.Center,
+            content = content
+        )
+    }
+}
+
+/**
+ * Transition defining how the tint color for a tab animates, when a new tab is selected. This
+ * component uses [LocalContentColor] to provide an interpolated value between [activeColor]
+ * and [inactiveColor] depending on the animation status.
+ */
+@Composable
+private fun TabTransition(
+    activeColor: Color,
+    inactiveColor: Color,
+    selected: Boolean,
+    content: @Composable () -> Unit
+) {
+    val transition = updateTransition(selected)
+    val color by transition.animateColor(
+        transitionSpec = {
+            if (false isTransitioningTo true) {
+                tween(
+                    durationMillis = TabFadeInAnimationDuration,
+                    delayMillis = TabFadeInAnimationDelay,
+                    easing = LinearEasing
+                )
+            } else {
+                tween(
+                    durationMillis = TabFadeOutAnimationDuration,
+                    easing = LinearEasing
+                )
+            }
+        }
+    ) {
+        if (it) activeColor else inactiveColor
+    }
+    CompositionLocalProvider(
+        LocalContentColor provides color,
+        content = content
+    )
+}
+
+/**
+ * A [Layout] that positions [text] and an optional [icon] with the correct baseline distances. This
+ * Layout will either be [SmallTabHeight] or [LargeTabHeight] depending on its content, and then
+ * place the text and/or icon inside with the correct baseline alignment.
+ */
+@Composable
+private fun TabBaselineLayout(
+    text: @Composable (() -> Unit)?,
+    icon: @Composable (() -> Unit)?
+) {
+    Layout(
+        {
+            if (text != null) {
+                Box(
+                    Modifier.layoutId("text").padding(horizontal = HorizontalTextPadding)
+                ) { text() }
+            }
+            if (icon != null) {
+                Box(Modifier.layoutId("icon")) { icon() }
+            }
+        }
+    ) { measurables, constraints ->
+        val textPlaceable = text?.let {
+            measurables.first { it.layoutId == "text" }.measure(
+                // Measure with loose constraints for height as we don't want the text to take up more
+                // space than it needs
+                constraints.copy(minHeight = 0)
+            )
+        }
+
+        val iconPlaceable = icon?.let {
+            measurables.first { it.layoutId == "icon" }.measure(constraints)
+        }
+
+        val tabWidth = max(textPlaceable?.width ?: 0, iconPlaceable?.width ?: 0)
+
+        val tabHeight = if (textPlaceable != null && iconPlaceable != null) {
+            LargeTabHeight
+        } else {
+            SmallTabHeight
+        }.roundToPx()
+
+        val firstBaseline = textPlaceable?.get(FirstBaseline)
+        val lastBaseline = textPlaceable?.get(LastBaseline)
+
+        layout(tabWidth, tabHeight) {
+            when {
+                textPlaceable != null && iconPlaceable != null -> placeTextAndIcon(
+                    density = this@Layout,
+                    textPlaceable = textPlaceable,
+                    iconPlaceable = iconPlaceable,
+                    tabWidth = tabWidth,
+                    tabHeight = tabHeight,
+                    firstBaseline = firstBaseline!!,
+                    lastBaseline = lastBaseline!!
+                )
+                textPlaceable != null -> placeTextOrIcon(textPlaceable, tabHeight)
+                iconPlaceable != null -> placeTextOrIcon(iconPlaceable, tabHeight)
+                else -> {
+                }
+            }
+        }
+    }
+}
+
+/**
+ * Places the provided [textOrIconPlaceable] in the vertical center of the provided
+ * [tabHeight].
+ */
+private fun Placeable.PlacementScope.placeTextOrIcon(
+    textOrIconPlaceable: Placeable,
+    tabHeight: Int
+) {
+    val contentY = (tabHeight - textOrIconPlaceable.height) / 2
+    textOrIconPlaceable.placeRelative(0, contentY)
+}
+
+/**
+ * Places the provided [textPlaceable] offset from the bottom of the tab using the correct
+ * baseline offset, with the provided [iconPlaceable] placed above the text using the correct
+ * baseline offset.
+ */
+private fun Placeable.PlacementScope.placeTextAndIcon(
+    density: Density,
+    textPlaceable: Placeable,
+    iconPlaceable: Placeable,
+    tabWidth: Int,
+    tabHeight: Int,
+    firstBaseline: Int,
+    lastBaseline: Int
+) {
+    val baselineOffset = if (firstBaseline == lastBaseline) {
+        SingleLineTextBaselineWithIcon
+    } else {
+        DoubleLineTextBaselineWithIcon
+    }
+
+    // Total offset between the last text baseline and the bottom of the Tab layout
+    val textOffset = with(density) {
+        baselineOffset.roundToPx() + PrimaryNavigationTabTokens.ActiveIndicatorHeight.roundToPx()
+    }
+
+    // How much space there is between the top of the icon (essentially the top of this layout)
+    // and the top of the text layout's bounding box (not baseline)
+    val iconOffset = with(density) {
+        iconPlaceable.height + IconDistanceFromBaseline.roundToPx() - firstBaseline
+    }
+
+    val textPlaceableX = (tabWidth - textPlaceable.width) / 2
+    val textPlaceableY = tabHeight - lastBaseline - textOffset
+    textPlaceable.placeRelative(textPlaceableX, textPlaceableY)
+
+    val iconPlaceableX = (tabWidth - iconPlaceable.width) / 2
+    val iconPlaceableY = textPlaceableY - iconOffset
+    iconPlaceable.placeRelative(iconPlaceableX, iconPlaceableY)
+}
+
+// Tab specifications
+private val SmallTabHeight = PrimaryNavigationTabTokens.ContainerHeight
+private val LargeTabHeight = 72.dp
+
+// Tab transition specifications
+private const val TabFadeInAnimationDuration = 150
+private const val TabFadeInAnimationDelay = 100
+private const val TabFadeOutAnimationDuration = 100
+
+// The horizontal padding on the left and right of text
+private val HorizontalTextPadding = 16.dp
+
+// Distance from the top of the indicator to the text baseline when there is one line of text and an
+// icon
+private val SingleLineTextBaselineWithIcon = 14.dp
+// Distance from the top of the indicator to the last text baseline when there are two lines of text
+// and an icon
+private val DoubleLineTextBaselineWithIcon = 6.dp
+// Distance from the first text baseline to the bottom of the icon in a combined tab
+private val IconDistanceFromBaseline = 20.sp
+// Distance from the end of the leading icon to the start of the text
+private val TextDistanceFromLeadingIcon = 8.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
new file mode 100644
index 0000000..42d27f6
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
@@ -0,0 +1,500 @@
+/*
+ * Copyright 2022 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
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.background
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
+import androidx.compose.material3.tokens.PrimaryNavigationTabTokens
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.SubcomposeLayout
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+// TODO: Provide M3 tab row image when asset is available.
+/**
+ * <a href="https://material.io/components/tabs#fixed-tabs" class="external" target="_blank">Material Design fixed tabs</a>.
+ *
+ * Fixed tabs display all tabs in a set simultaneously. They are best for switching between related
+ * content quickly, such as between transportation methods in a map. To navigate between fixed tabs,
+ * tap an individual tab, or swipe left or right in the content area.
+ *
+ * A TabRow contains a row of [Tab]s, and displays an indicator underneath the currently
+ * selected tab. A TabRow places its tabs evenly spaced along the entire row, with each tab
+ * taking up an equal amount of space. See [ScrollableTabRow] for a tab row that does not enforce
+ * equal size, and allows scrolling to tabs that do not fit on screen.
+ *
+ * A simple example with text tabs looks like:
+ *
+ * @sample androidx.compose.material3.samples.TextTabs
+ *
+ * You can also provide your own custom tab, such as:
+ *
+ * @sample androidx.compose.material3.samples.FancyTabs
+ *
+ * Where the custom tab itself could look like:
+ *
+ * @sample androidx.compose.material3.samples.FancyTab
+ *
+ * As well as customizing the tab, you can also provide a custom [indicator], to customize
+ * the indicator displayed for a tab. [indicator] will be placed to fill the entire TabRow, so it
+ * should internally take care of sizing and positioning the indicator to match changes to
+ * [selectedTabIndex].
+ *
+ * For example, given an indicator that draws a rounded rectangle near the edges of the [Tab]:
+ *
+ * @sample androidx.compose.material3.samples.FancyIndicator
+ *
+ * We can reuse [TabRowDefaults.tabIndicatorOffset] and just provide this indicator,
+ * as we aren't changing how the size and position of the indicator changes between tabs:
+ *
+ * @sample androidx.compose.material3.samples.FancyIndicatorTabs
+ *
+ * You may also want to use a custom transition, to allow you to dynamically change the
+ * appearance of the indicator as it animates between tabs, such as changing its color or size.
+ * [indicator] is stacked on top of the entire TabRow, so you just need to provide a custom
+ * transition that animates the offset of the indicator from the start of the TabRow. For
+ * example, take the following example that uses a transition to animate the offset, width, and
+ * color of the same FancyIndicator from before, also adding a physics based 'spring' effect to
+ * the indicator in the direction of motion:
+ *
+ * @sample androidx.compose.material3.samples.FancyAnimatedIndicator
+ *
+ * We can now just pass this indicator directly to TabRow:
+ *
+ * @sample androidx.compose.material3.samples.FancyIndicatorContainerTabs
+ *
+ * @param selectedTabIndex the index of the currently selected tab
+ * @param modifier optional [Modifier] for this TabRow
+ * @param containerColor The color of the container for the TabRow. Use [Color.Transparent] to have
+ * no color.
+ * @param contentColor The preferred content color provided by this TabRow to its children.
+ * Defaults to either the matching content color for [containerColor], or if [containerColor] is
+ * not a color from the theme, this will keep the same value set above this TabRow.
+ * @param indicator the indicator that represents which tab is currently selected. By default this
+ * will be a [TabRowDefaults.Indicator], using a [TabRowDefaults.tabIndicatorOffset]
+ * modifier to animate its position. Note that this indicator will be forced to fill up the
+ * entire TabRow, so you should use [TabRowDefaults.tabIndicatorOffset] or similar to
+ * animate the actual drawn indicator inside this space, and provide an offset from the start.
+ * @param divider the divider displayed at the bottom of the TabRow. This provides a layer of
+ * separation between the TabRow and the content displayed underneath.
+ * @param tabs the tabs inside this TabRow. Typically this will be multiple [Tab]s. Each element
+ * inside this lambda will be measured and placed evenly across the TabRow, each taking up equal
+ * space.
+ */
+@Composable
+fun TabRow(
+    selectedTabIndex: Int,
+    modifier: Modifier = Modifier,
+    containerColor: Color =
+        MaterialTheme.colorScheme.fromToken(PrimaryNavigationTabTokens.ContainerColor),
+    contentColor: Color =
+        MaterialTheme.colorScheme.fromToken(PrimaryNavigationTabTokens.ActiveLabelTextColor),
+    indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
+        TabRowDefaults.Indicator(
+            Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
+        )
+    },
+    divider: @Composable () -> Unit = @Composable {
+        TabRowDefaults.Divider()
+    },
+    tabs: @Composable () -> Unit
+) {
+    Surface(
+        modifier = modifier.selectableGroup(),
+        color = containerColor,
+        contentColor = contentColor
+    ) {
+        SubcomposeLayout(Modifier.fillMaxWidth()) { constraints ->
+            val tabRowWidth = constraints.maxWidth
+            val tabMeasurables = subcompose(TabSlots.Tabs, tabs)
+            val tabCount = tabMeasurables.size
+            val tabWidth = (tabRowWidth / tabCount)
+            val tabPlaceables = tabMeasurables.map {
+                it.measure(constraints.copy(minWidth = tabWidth, maxWidth = tabWidth))
+            }
+
+            val tabRowHeight = tabPlaceables.maxByOrNull { it.height }?.height ?: 0
+
+            val tabPositions = List(tabCount) { index ->
+                TabPosition(tabWidth.toDp() * index, tabWidth.toDp())
+            }
+
+            layout(tabRowWidth, tabRowHeight) {
+                tabPlaceables.forEachIndexed { index, placeable ->
+                    placeable.placeRelative(index * tabWidth, 0)
+                }
+
+                subcompose(TabSlots.Divider, divider).forEach {
+                    val placeable = it.measure(constraints.copy(minHeight = 0))
+                    placeable.placeRelative(0, tabRowHeight - placeable.height)
+                }
+
+                subcompose(TabSlots.Indicator) {
+                    indicator(tabPositions)
+                }.forEach {
+                    it.measure(Constraints.fixed(tabRowWidth, tabRowHeight)).placeRelative(0, 0)
+                }
+            }
+        }
+    }
+}
+
+// TODO: Provide M3 tab row image when asset is available.
+/**
+ * <a href="https://material.io/components/tabs#scrollable-tabs" class="external" target="_blank">Material Design scrollable tabs</a>.
+ *
+ * When a set of tabs cannot fit on screen, use scrollable tabs. Scrollable tabs can use longer text
+ * labels and a larger number of tabs. They are best used for browsing on touch interfaces.
+ *
+ * A ScrollableTabRow contains a row of [Tab]s, and displays an indicator underneath the currently
+ * selected tab. A ScrollableTabRow places its tabs offset from the starting edge, and allows
+ * scrolling to tabs that are placed off screen. For a fixed tab row that does not allow
+ * scrolling, and evenly places its tabs, see [TabRow].
+ *
+ * @param selectedTabIndex the index of the currently selected tab
+ * @param modifier optional [Modifier] for this ScrollableTabRow
+ * @param containerColor The background color for the ScrollableTabRow. Use [Color.Transparent] to
+ * have no color.
+ * @param contentColor The preferred content color provided by this ScrollableTabRow to its
+ * children. Defaults to either the matching content color for [containerColor], or if
+ * [containerColor] is not a color from the theme, this will keep the same value set above this
+ * ScrollableTabRow.
+ * @param edgePadding the padding between the starting and ending edge of ScrollableTabRow, and
+ * the tabs inside the ScrollableTabRow. This padding helps inform the user that this tab row can
+ * be scrolled, unlike a [TabRow].
+ * @param indicator the indicator that represents which tab is currently selected. By default this
+ * will be a [TabRowDefaults.Indicator], using a [TabRowDefaults.tabIndicatorOffset]
+ * modifier to animate its position. Note that this indicator will be forced to fill up the
+ * entire ScrollableTabRow, so you should use [TabRowDefaults.tabIndicatorOffset] or similar to
+ * animate the actual drawn indicator inside this space, and provide an offset from the start.
+ * @param divider the divider displayed at the bottom of the ScrollableTabRow. This provides a layer
+ * of separation between the ScrollableTabRow and the content displayed underneath.
+ * @param tabs the tabs inside this ScrollableTabRow. Typically this will be multiple [Tab]s. Each
+ * element inside this lambda will be measured and placed evenly across the TabRow, each taking
+ * up equal space.
+ */
+@Composable
+fun ScrollableTabRow(
+    selectedTabIndex: Int,
+    modifier: Modifier = Modifier,
+    containerColor: Color =
+        MaterialTheme.colorScheme.fromToken(PrimaryNavigationTabTokens.ContainerColor),
+    contentColor: Color =
+        MaterialTheme.colorScheme.fromToken(PrimaryNavigationTabTokens.ActiveLabelTextColor),
+    edgePadding: Dp = ScrollableTabRowPadding,
+    indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
+        TabRowDefaults.Indicator(
+            Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
+        )
+    },
+    divider: @Composable () -> Unit = @Composable {
+        TabRowDefaults.Divider()
+    },
+    tabs: @Composable () -> Unit
+) {
+    Surface(
+        modifier = modifier,
+        color = containerColor,
+        contentColor = contentColor
+    ) {
+        val scrollState = rememberScrollState()
+        val coroutineScope = rememberCoroutineScope()
+        val scrollableTabData = remember(scrollState, coroutineScope) {
+            ScrollableTabData(
+                scrollState = scrollState,
+                coroutineScope = coroutineScope
+            )
+        }
+        SubcomposeLayout(
+            Modifier.fillMaxWidth()
+                .wrapContentSize(align = Alignment.CenterStart)
+                .horizontalScroll(scrollState)
+                .selectableGroup()
+                .clipToBounds()
+        ) { constraints ->
+            val minTabWidth = ScrollableTabRowMinimumTabWidth.roundToPx()
+            val padding = edgePadding.roundToPx()
+            val tabConstraints = constraints.copy(minWidth = minTabWidth)
+
+            val tabPlaceables = subcompose(TabSlots.Tabs, tabs)
+                .map { it.measure(tabConstraints) }
+
+            var layoutWidth = padding * 2
+            var layoutHeight = 0
+            tabPlaceables.forEach {
+                layoutWidth += it.width
+                layoutHeight = maxOf(layoutHeight, it.height)
+            }
+
+            // Position the children.
+            layout(layoutWidth, layoutHeight) {
+                // Place the tabs
+                val tabPositions = mutableListOf<TabPosition>()
+                var left = padding
+                tabPlaceables.forEach {
+                    it.placeRelative(left, 0)
+                    tabPositions.add(TabPosition(left = left.toDp(), width = it.width.toDp()))
+                    left += it.width
+                }
+
+                // The divider is measured with its own height, and width equal to the total width
+                // of the tab row, and then placed on top of the tabs.
+                subcompose(TabSlots.Divider, divider).forEach {
+                    val placeable = it.measure(
+                        constraints.copy(
+                            minHeight = 0,
+                            minWidth = layoutWidth,
+                            maxWidth = layoutWidth
+                        )
+                    )
+                    placeable.placeRelative(0, layoutHeight - placeable.height)
+                }
+
+                // The indicator container is measured to fill the entire space occupied by the tab
+                // row, and then placed on top of the divider.
+                subcompose(TabSlots.Indicator) {
+                    indicator(tabPositions)
+                }.forEach {
+                    it.measure(Constraints.fixed(layoutWidth, layoutHeight)).placeRelative(0, 0)
+                }
+
+                scrollableTabData.onLaidOut(
+                    density = this@SubcomposeLayout,
+                    edgeOffset = padding,
+                    tabPositions = tabPositions,
+                    selectedTab = selectedTabIndex
+                )
+            }
+        }
+    }
+}
+
+/**
+ * Data class that contains information about a tab's position on screen, used for calculating
+ * where to place the indicator that shows which tab is selected.
+ *
+ * @property left the left edge's x position from the start of the [TabRow]
+ * @property right the right edge's x position from the start of the [TabRow]
+ * @property width the width of this tab
+ */
+@Immutable
+class TabPosition internal constructor(val left: Dp, val width: Dp) {
+    val right: Dp get() = left + width
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is TabPosition) return false
+
+        if (left != other.left) return false
+        if (width != other.width) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = left.hashCode()
+        result = 31 * result + width.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "TabPosition(left=$left, right=$right, width=$width)"
+    }
+}
+
+/**
+ * Contains default implementations and values used for TabRow.
+ */
+object TabRowDefaults {
+    /**
+     * Default [Divider], which will be positioned at the bottom of the [TabRow], underneath the
+     * indicator.
+     *
+     * @param modifier modifier for the divider's layout
+     * @param thickness thickness of the divider
+     * @param color color of the divider
+     */
+    @Composable
+    fun Divider(
+        modifier: Modifier = Modifier,
+        thickness: Dp = PrimaryNavigationTabTokens.DividerHeight,
+        color: Color =
+            MaterialTheme.colorScheme.fromToken(PrimaryNavigationTabTokens.DividerColor)
+    ) {
+        androidx.compose.material3.Divider(
+            modifier = modifier, thickness = thickness, color = color)
+    }
+
+    /**
+     * Default indicator, which will be positioned at the bottom of the [TabRow], on top of the
+     * divider.
+     *
+     * @param modifier modifier for the indicator's layout
+     * @param height height of the indicator
+     * @param color color of the indicator
+     */
+    @Composable
+    fun Indicator(
+        modifier: Modifier = Modifier,
+        height: Dp = PrimaryNavigationTabTokens.ActiveIndicatorHeight,
+        color: Color =
+            MaterialTheme.colorScheme.fromToken(PrimaryNavigationTabTokens.ActiveIndicatorColor)
+    ) {
+        Box(
+            modifier
+                .fillMaxWidth()
+                .height(height)
+                .background(color = color)
+        )
+    }
+
+    /**
+     * [Modifier] that takes up all the available width inside the [TabRow], and then animates
+     * the offset of the indicator it is applied to, depending on the [currentTabPosition].
+     *
+     * @param currentTabPosition [TabPosition] of the currently selected tab. This is used to
+     * calculate the offset of the indicator this modifier is applied to, as well as its width.
+     */
+    fun Modifier.tabIndicatorOffset(
+        currentTabPosition: TabPosition
+    ): Modifier = composed(
+        inspectorInfo = debugInspectorInfo {
+            name = "tabIndicatorOffset"
+            value = currentTabPosition
+        }
+    ) {
+        val currentTabWidth by animateDpAsState(
+            targetValue = currentTabPosition.width,
+            animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing)
+        )
+        val indicatorOffset by animateDpAsState(
+            targetValue = currentTabPosition.left,
+            animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing)
+        )
+        fillMaxWidth()
+            .wrapContentSize(Alignment.BottomStart)
+            .offset(x = indicatorOffset)
+            .width(currentTabWidth)
+    }
+}
+
+private enum class TabSlots {
+    Tabs,
+    Divider,
+    Indicator
+}
+
+/**
+ * Class holding onto state needed for [ScrollableTabRow]
+ */
+private class ScrollableTabData(
+    private val scrollState: ScrollState,
+    private val coroutineScope: CoroutineScope
+) {
+    private var selectedTab: Int? = null
+
+    fun onLaidOut(
+        density: Density,
+        edgeOffset: Int,
+        tabPositions: List<TabPosition>,
+        selectedTab: Int
+    ) {
+        // Animate if the new tab is different from the old tab, or this is called for the first
+        // time (i.e selectedTab is `null`).
+        if (this.selectedTab != selectedTab) {
+            this.selectedTab = selectedTab
+            tabPositions.getOrNull(selectedTab)?.let {
+                // Scrolls to the tab with [tabPosition], trying to place it in the center of the
+                // screen or as close to the center as possible.
+                val calculatedOffset = it.calculateTabOffset(density, edgeOffset, tabPositions)
+                if (scrollState.value != calculatedOffset) {
+                    coroutineScope.launch {
+                        scrollState.animateScrollTo(
+                            calculatedOffset,
+                            animationSpec = ScrollableTabRowScrollSpec
+                        )
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @return the offset required to horizontally center the tab inside this TabRow.
+     * If the tab is at the start / end, and there is not enough space to fully centre the tab, this
+     * will just clamp to the min / max position given the max width.
+     */
+    private fun TabPosition.calculateTabOffset(
+        density: Density,
+        edgeOffset: Int,
+        tabPositions: List<TabPosition>
+    ): Int = with(density) {
+        val totalTabRowWidth = tabPositions.last().right.roundToPx() + edgeOffset
+        val visibleWidth = totalTabRowWidth - scrollState.maxValue
+        val tabOffset = left.roundToPx()
+        val scrollerCenter = visibleWidth / 2
+        val tabWidth = width.roundToPx()
+        val centeredTabOffset = tabOffset - (scrollerCenter - tabWidth / 2)
+        // How much space we have to scroll. If the visible width is <= to the total width, then
+        // we have no space to scroll as everything is always visible.
+        val availableSpace = (totalTabRowWidth - visibleWidth).coerceAtLeast(0)
+        return centeredTabOffset.coerceIn(0, availableSpace)
+    }
+}
+
+private val ScrollableTabRowMinimumTabWidth = 90.dp
+
+/**
+ * The default padding from the starting edge before a tab in a [ScrollableTabRow].
+ */
+private val ScrollableTabRowPadding = 52.dp
+
+/**
+ * [AnimationSpec] used when scrolling to a tab that is not fully visible.
+ */
+private val ScrollableTabRowScrollSpec: AnimationSpec<Float> = tween(
+    durationMillis = 250,
+    easing = FastOutSlowInEasing
+)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/BadgeTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/BadgeTokens.kt
index 40bd2f2..6236ce1 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/BadgeTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/BadgeTokens.kt
@@ -13,13 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
-// TO MODIFY THIS FILE UPDATE:
-//   ux/material/dsdb/contrib/generators/tokens/src/compose/templates/component.jinja2
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object BadgeTokens {
@@ -27,8 +25,8 @@
     val LargeColor = ColorSchemeKeyTokens.Error
     val LargeLabelTextColor = ColorSchemeKeyTokens.OnError
     val LargeLabelTextFont = TypographyKeyTokens.LabelSmall
-    val LargeShape = RoundedCornerShape(8.0.dp)
+    val LargeShape = ShapeTokens.CornerFull
     val LargeSize = 16.0.dp
-    val Shape = RoundedCornerShape(3.0.dp)
+    val Shape = ShapeTokens.CornerFull
     val Size = 6.0.dp
 }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CheckboxTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CheckboxTokens.kt
index dbe9eaa..777aaef 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CheckboxTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CheckboxTokens.kt
@@ -13,13 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object CheckboxTokens {
+    val ContainerShape = RoundedCornerShape(2.0.dp)
     val DisabledSelectedIconColor = ColorSchemeKeyTokens.Primary
     const val DisabledSelectedIconOpacity = 0.38f
     val DisabledUnselectedIconColor = ColorSchemeKeyTokens.Primary
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CircularProgressIndicatorTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CircularProgressIndicatorTokens.kt
index 3d5fe24..5952b60 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CircularProgressIndicatorTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CircularProgressIndicatorTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -21,6 +22,7 @@
 
 internal object CircularProgressIndicatorTokens {
     val ActiveIndicatorColor = ColorSchemeKeyTokens.Primary
+    val ActiveShape = ShapeTokens.CornerNone
     val ActiveIndicatorWidth = 4.0.dp
     val FourColorActiveIndicatorFourColor = ColorSchemeKeyTokens.TertiaryContainer
     val FourColorActiveIndicatorOneColor = ColorSchemeKeyTokens.Primary
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorDarkTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorDarkTokens.kt
index d7a1e8b..6b0f41d 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorDarkTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorDarkTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorLightTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorLightTokens.kt
index 41e2ae8..bb04d20 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorLightTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorLightTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorSchemeKeyTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorSchemeKeyTokens.kt
index 1cd5f5f..f72297c 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorSchemeKeyTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ColorSchemeKeyTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/DialogTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/DialogTokens.kt
index be70c69..20f4f45 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/DialogTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/DialogTokens.kt
@@ -13,11 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object DialogTokens {
@@ -31,7 +31,7 @@
     val ActionPressedStateLayerColor = ColorSchemeKeyTokens.Primary
     val ContainerColor = ColorSchemeKeyTokens.Surface
     val ContainerElevation = ElevationTokens.Level3
-    val ContainerShape = RoundedCornerShape(28.0.dp)
+    val ContainerShape = ShapeTokens.CornerExtraLarge
     val SubheadColor = ColorSchemeKeyTokens.OnSurface
     val SubheadFont = TypographyKeyTokens.HeadlineSmall
     val SupportingTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevatedButtonTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevatedButtonTokens.kt
index bbdb795..e2e040c 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevatedButtonTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevatedButtonTokens.kt
@@ -13,18 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object ElevatedButtonTokens {
     val ContainerColor = ColorSchemeKeyTokens.Surface
     val ContainerElevation = ElevationTokens.Level1
     val ContainerHeight = 40.0.dp
-    val ContainerShape = RoundedCornerShape(20.0.dp)
+    val ContainerShape = ShapeTokens.CornerFull
     val DisabledContainerColor = ColorSchemeKeyTokens.OnSurface
     val DisabledContainerElevation = ElevationTokens.Level0
     const val DisabledContainerOpacity = 0.12f
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevatedCardTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevatedCardTokens.kt
index 806f27d..e3b14c6 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevatedCardTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevatedCardTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -22,7 +23,7 @@
 internal object ElevatedCardTokens {
     val ContainerColor = ColorSchemeKeyTokens.Surface
     val ContainerElevation = ElevationTokens.Level1
-    val ContainerShape = ShapeTokens.Medium
+    val ContainerShape = ShapeTokens.CornerMedium
     val DraggedContainerElevation = ElevationTokens.Level4
     val DraggedStateLayerColor = ColorSchemeKeyTokens.OnSurface
     val FocusContainerElevation = ElevationTokens.Level1
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevationTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevationTokens.kt
index a301b6d..01485ae 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevationTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ElevationTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ExtendedFabPrimaryTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ExtendedFabPrimaryTokens.kt
index c38ed9c..612d634 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ExtendedFabPrimaryTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ExtendedFabPrimaryTokens.kt
@@ -13,18 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object ExtendedFabPrimaryTokens {
     val ContainerColor = ColorSchemeKeyTokens.PrimaryContainer
     val ContainerElevation = ElevationTokens.Level3
     val ContainerHeight = 56.0.dp
-    val ContainerShape = RoundedCornerShape(16.0.dp)
+    val ContainerShape = ShapeTokens.CornerLarge
     val FocusContainerElevation = ElevationTokens.Level3
     val FocusIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
     val FocusLabelTextColor = ColorSchemeKeyTokens.OnPrimaryContainer
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimaryLargeTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimaryLargeTokens.kt
index e0d825a..e3f22b2 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimaryLargeTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimaryLargeTokens.kt
@@ -13,18 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object FabPrimaryLargeTokens {
     val ContainerColor = ColorSchemeKeyTokens.PrimaryContainer
     val ContainerElevation = ElevationTokens.Level3
     val ContainerHeight = 96.0.dp
-    val ContainerShape = RoundedCornerShape(28.0.dp)
+    val ContainerShape = ShapeTokens.CornerExtraLarge
     val ContainerWidth = 96.0.dp
     val FocusContainerElevation = ElevationTokens.Level3
     val FocusIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimarySmallTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimarySmallTokens.kt
index 2fe18e68..61c9ac3 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimarySmallTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimarySmallTokens.kt
@@ -13,18 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object FabPrimarySmallTokens {
     val ContainerColor = ColorSchemeKeyTokens.PrimaryContainer
     val ContainerElevation = ElevationTokens.Level3
     val ContainerHeight = 40.0.dp
-    val ContainerShape = RoundedCornerShape(12.0.dp)
+    val ContainerShape = ShapeTokens.CornerMedium
     val ContainerWidth = 40.0.dp
     val FocusContainerElevation = ElevationTokens.Level3
     val FocusIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimaryTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimaryTokens.kt
index c34d2f1..0b95df5 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimaryTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FabPrimaryTokens.kt
@@ -13,18 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object FabPrimaryTokens {
     val ContainerColor = ColorSchemeKeyTokens.PrimaryContainer
     val ContainerElevation = ElevationTokens.Level3
     val ContainerHeight = 56.0.dp
-    val ContainerShape = RoundedCornerShape(16.0.dp)
+    val ContainerShape = ShapeTokens.CornerLarge
     val ContainerWidth = 56.0.dp
     val FocusContainerElevation = ElevationTokens.Level3
     val FocusIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledButtonTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledButtonTokens.kt
index 74eea7a..03d54ec 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledButtonTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledButtonTokens.kt
@@ -13,18 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object FilledButtonTokens {
     val ContainerColor = ColorSchemeKeyTokens.Primary
     val ContainerElevation = ElevationTokens.Level0
     val ContainerHeight = 40.0.dp
-    val ContainerShape = RoundedCornerShape(20.0.dp)
+    val ContainerShape = ShapeTokens.CornerFull
     val DisabledContainerColor = ColorSchemeKeyTokens.OnSurface
     val DisabledContainerElevation = ElevationTokens.Level0
     const val DisabledContainerOpacity = 0.12f
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledCardTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledCardTokens.kt
index a4d59b7..13c9338 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledCardTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledCardTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -22,7 +23,7 @@
 internal object FilledCardTokens {
     val ContainerColor = ColorSchemeKeyTokens.SurfaceVariant
     val ContainerElevation = ElevationTokens.Level0
-    val ContainerShape = ShapeTokens.Medium
+    val ContainerShape = ShapeTokens.CornerMedium
     val DraggedContainerElevation = ElevationTokens.Level3
     val DraggedStateLayerColor = ColorSchemeKeyTokens.OnSurface
     val FocusContainerElevation = ElevationTokens.Level0
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledButtonTonalTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledTonalButtonTokens.kt
similarity index 92%
rename from compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledButtonTonalTokens.kt
rename to compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledTonalButtonTokens.kt
index 4835f99..4005b7e 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledButtonTonalTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilledTonalButtonTokens.kt
@@ -13,19 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
-internal object FilledButtonTonalTokens {
+internal object FilledTonalButtonTokens {
     val ContainerColor = ColorSchemeKeyTokens.SecondaryContainer
     val ContainerElevation = ElevationTokens.Level0
     val ContainerHeight = 40.0.dp
-    val ContainerShape = RoundedCornerShape(20.0.dp)
+    val ContainerShape = ShapeTokens.CornerFull
     val DisabledContainerColor = ColorSchemeKeyTokens.OnSurface
+    val DisabledContainerElevation = ElevationTokens.Level0
     const val DisabledContainerOpacity = 0.12f
     val DisabledLabelTextColor = ColorSchemeKeyTokens.OnSurface
     const val DisabledLabelTextOpacity = 0.38f
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/IconButtonTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/IconButtonTokens.kt
index 2720a0e..6dc4fea 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/IconButtonTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/IconButtonTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -22,13 +23,21 @@
 internal object IconButtonTokens {
     val DisabledIconColor = ColorSchemeKeyTokens.OnSurface
     const val DisabledIconOpacity = 0.38f
-    val FocusIconColor = ColorSchemeKeyTokens.Primary
-    val FocusStateLayerColor = ColorSchemeKeyTokens.Primary
-    val HoverIconColor = ColorSchemeKeyTokens.Primary
-    val HoverStateLayerColor = ColorSchemeKeyTokens.Primary
-    val IconColor = ColorSchemeKeyTokens.Primary
     val IconSize = 24.0.dp
-    val PressedIconColor = ColorSchemeKeyTokens.Primary
-    val PressedStateLayerColor = ColorSchemeKeyTokens.Primary
+    val SelectedFocusIconColor = ColorSchemeKeyTokens.Primary
+    val SelectedFocusStateLayerColor = ColorSchemeKeyTokens.Primary
+    val SelectedHoverIconColor = ColorSchemeKeyTokens.Primary
+    val SelectedHoverStateLayerColor = ColorSchemeKeyTokens.Primary
+    val SelectedIconColor = ColorSchemeKeyTokens.Primary
+    val SelectedPressedIconColor = ColorSchemeKeyTokens.Primary
+    val SelectedPressedStateLayerColor = ColorSchemeKeyTokens.Primary
+    val StateLayerShape = ShapeTokens.CornerFull
     val StateLayerSize = 40.0.dp
+    val UnselectedFocusIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedFocusStateLayerColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedHoverIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedHoverStateLayerColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedPressedIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedPressedStateLayerColor = ColorSchemeKeyTokens.OnSurfaceVariant
 }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LinearProgressIndicatorTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LinearProgressIndicatorTokens.kt
index c8c47266..eda5234 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LinearProgressIndicatorTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LinearProgressIndicatorTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -22,10 +23,12 @@
 internal object LinearProgressIndicatorTokens {
     val ActiveIndicatorColor = ColorSchemeKeyTokens.Primary
     val ActiveIndicatorHeight = 4.0.dp
+    val ActiveShape = ShapeTokens.CornerNone
     val FourColorActiveIndicatorFourColor = ColorSchemeKeyTokens.TertiaryContainer
     val FourColorActiveIndicatorOneColor = ColorSchemeKeyTokens.Primary
     val FourColorActiveIndicatorThreeColor = ColorSchemeKeyTokens.Tertiary
     val FourColorActiveIndicatorTwoColor = ColorSchemeKeyTokens.PrimaryContainer
     val TrackColor = ColorSchemeKeyTokens.SurfaceVariant
     val TrackHeight = 4.0.dp
+    val TrackShape = ShapeTokens.CornerNone
 }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/MenuTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/MenuTokens.kt
new file mode 100644
index 0000000..f5d5f44
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/MenuTokens.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 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.
+ */
+// VERSION: v0_76
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.compose.material3.tokens
+
+import androidx.compose.ui.unit.dp
+
+internal object MenuTokens {
+    val ContainerColor = ColorSchemeKeyTokens.Surface
+    val ContainerElevation = ElevationTokens.Level2
+    val ContainerShape = ShapeTokens.CornerExtraSmall
+    val DividerColor = ColorSchemeKeyTokens.SurfaceVariant
+    val DividerHeight = 1.0.dp
+    val ListItemContainerHeight = 48.0.dp
+    val ListItemDisabledLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    const val ListItemDisabledLabelTextOpacity = 0.38f
+    val ListItemFocusLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val ListItemFocusStateLayerColor = ColorSchemeKeyTokens.OnSurface
+    val ListItemHoverLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val ListItemHoverStateLayerColor = ColorSchemeKeyTokens.OnSurface
+    val ListItemLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val ListItemLabelTextFont = TypographyKeyTokens.LabelLarge
+    val ListItemPressedLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val ListItemPressedStateLayerColor = ColorSchemeKeyTokens.OnSurface
+    val ListItemSelectedContainerColor = ColorSchemeKeyTokens.SurfaceVariant
+    val ListItemDisabledLeadingIconColor = ColorSchemeKeyTokens.OnSurface
+    const val ListItemDisabledLeadingIconOpacity = 0.38f
+    val ListItemLeadingFocusIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val ListItemLeadingHoverIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val ListItemLeadingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val ListItemLeadingIconSize = 24.0.dp
+    val ListItemLeadingPressedIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val ListItemDisabledTrailingIconColor = ColorSchemeKeyTokens.OnSurface
+    const val ListItemDisabledTrailingIconOpacity = 0.38f
+    val ListItemTrailingFocusIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val ListItemTrailingHoverIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val ListItemTrailingPressedIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val ListItemTrailingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val ListItemTrailingIconSize = 24.0.dp
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationBarTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationBarTokens.kt
index 72dae76..1206292 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationBarTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationBarTokens.kt
@@ -13,32 +13,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object NavigationBarTokens {
-    val ActiveFocusIconColor = ColorSchemeKeyTokens.OnSecondary
+    val ActiveFocusIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActiveFocusLabelTextColor = ColorSchemeKeyTokens.OnSurface
     val ActiveFocusStateLayerColor = ColorSchemeKeyTokens.OnSurface
-    val ActiveHoverIconColor = ColorSchemeKeyTokens.OnSecondary
+    val ActiveHoverIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActiveHoverLabelTextColor = ColorSchemeKeyTokens.OnSurface
     val ActiveHoverStateLayerColor = ColorSchemeKeyTokens.OnSurface
     val ActiveIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActiveIndicatorColor = ColorSchemeKeyTokens.SecondaryContainer
     val ActiveIndicatorHeight = 32.0.dp
-    val ActiveIndicatorShape = RoundedCornerShape(16.0.dp)
+    val ActiveIndicatorShape = ShapeTokens.CornerLarge
     val ActiveIndicatorWidth = 64.0.dp
     val ActiveLabelTextColor = ColorSchemeKeyTokens.OnSurface
-    val ActivePressedIconColor = ColorSchemeKeyTokens.OnSecondary
+    val ActivePressedIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActivePressedLabelTextColor = ColorSchemeKeyTokens.OnSurface
     val ActivePressedStateLayerColor = ColorSchemeKeyTokens.OnSurface
     val ContainerColor = ColorSchemeKeyTokens.Surface
     val ContainerElevation = ElevationTokens.Level2
     val ContainerHeight = 80.0.dp
+    val ContainerShape = ShapeTokens.CornerNone
     val IconSize = 24.0.dp
     val InactiveFocusIconColor = ColorSchemeKeyTokens.OnSurface
     val InactiveFocusLabelTextColor = ColorSchemeKeyTokens.OnSurface
@@ -52,4 +53,4 @@
     val InactivePressedLabelTextColor = ColorSchemeKeyTokens.OnSurface
     val InactivePressedStateLayerColor = ColorSchemeKeyTokens.OnSurface
     val LabelTextFont = TypographyKeyTokens.LabelMedium
-}
\ No newline at end of file
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationDrawerTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationDrawerTokens.kt
index 7c47a58..d1faad5 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationDrawerTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationDrawerTokens.kt
@@ -13,11 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object NavigationDrawerTokens {
@@ -30,14 +30,16 @@
     val ActiveIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActiveIndicatorColor = ColorSchemeKeyTokens.SecondaryContainer
     val ActiveIndicatorHeight = 56.0.dp
-    val ActiveIndicatorShape = RoundedCornerShape(28.0.dp)
+    val ActiveIndicatorShape = ShapeTokens.CornerFull
     val ActiveIndicatorWidth = 336.0.dp
     val ActiveLabelTextColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActivePressedIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActivePressedLabelTextColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActivePressedStateLayerColor = ColorSchemeKeyTokens.OnSecondaryContainer
+    val BottomContainerShape = ShapeTokens.CornerLargeTop
     val ContainerColor = ColorSchemeKeyTokens.Surface
     const val ContainerHeightPercent = 100.0f
+    val ContainerShape = ShapeTokens.CornerLargeEnd
     val ContainerWidth = 360.0.dp
     val DividerColor = ColorSchemeKeyTokens.Outline
     val HeadlineColor = ColorSchemeKeyTokens.OnSurfaceVariant
@@ -55,7 +57,9 @@
     val InactivePressedLabelTextColor = ColorSchemeKeyTokens.OnSurface
     val InactivePressedStateLayerColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val LabelTextFont = TypographyKeyTokens.LabelLarge
+    val LargeBadgeLabelColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val LargeBadgeLabelFont = TypographyKeyTokens.LabelLarge
     val ModalContainerElevation = ElevationTokens.Level1
     const val ScrimOpacity = 0.4f
     val StandardContainerElevation = ElevationTokens.Level0
-}
\ No newline at end of file
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationRailTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationRailTokens.kt
index 33b4548..a0e3186 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationRailTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/NavigationRailTokens.kt
@@ -13,31 +13,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object NavigationRailTokens {
-    val ActiveFocusIconColor = ColorSchemeKeyTokens.OnSecondary
+    val ActiveFocusIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActiveFocusLabelTextColor = ColorSchemeKeyTokens.OnSurface
     val ActiveFocusStateLayerColor = ColorSchemeKeyTokens.OnSurface
-    val ActiveHoverIconColor = ColorSchemeKeyTokens.OnSecondary
+    val ActiveHoverIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActiveHoverLabelTextColor = ColorSchemeKeyTokens.OnSurface
     val ActiveHoverStateLayerColor = ColorSchemeKeyTokens.OnSurface
     val ActiveIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActiveIndicatorColor = ColorSchemeKeyTokens.SecondaryContainer
     val ActiveIndicatorHeight = 32.0.dp
-    val ActiveIndicatorShape = RoundedCornerShape(16.0.dp)
+    val ActiveIndicatorShape = ShapeTokens.CornerFull
     val ActiveIndicatorWidth = 56.0.dp
     val ActiveLabelTextColor = ColorSchemeKeyTokens.OnSurface
-    val ActivePressedIconColor = ColorSchemeKeyTokens.OnSecondary
+    val ActivePressedIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
     val ActivePressedLabelTextColor = ColorSchemeKeyTokens.OnSurface
     val ActivePressedStateLayerColor = ColorSchemeKeyTokens.OnSurface
     val ContainerColor = ColorSchemeKeyTokens.Surface
     val ContainerElevation = ElevationTokens.Level0
+    val ContainerShape = ShapeTokens.CornerNone
     val ContainerWidth = 80.0.dp
     val IconSize = 24.0.dp
     val InactiveFocusIconColor = ColorSchemeKeyTokens.OnSurface
@@ -61,5 +62,5 @@
     val MenuPressedIconColor = ColorSchemeKeyTokens.OnSurface
     val MenuPressedStateLayerColor = ColorSchemeKeyTokens.OnSurface
     val NoLabelActiveIndicatorHeight = 56.0.dp
-    val NoLabelActiveIndicatorShape = RoundedCornerShape(28.0.dp)
-}
\ No newline at end of file
+    val NoLabelActiveIndicatorShape = ShapeTokens.CornerFull
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/OutlinedButtonTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/OutlinedButtonTokens.kt
index 5bf7a15..c3c5192 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/OutlinedButtonTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/OutlinedButtonTokens.kt
@@ -13,16 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object OutlinedButtonTokens {
     val ContainerHeight = 40.0.dp
-    val ContainerShape = RoundedCornerShape(20.0.dp)
+    val ContainerShape = ShapeTokens.CornerFull
     val DisabledLabelTextColor = ColorSchemeKeyTokens.OnSurface
     const val DisabledLabelTextOpacity = 0.38f
     val DisabledOutlineColor = ColorSchemeKeyTokens.OnSurface
@@ -40,7 +40,6 @@
     val PressedLabelTextColor = ColorSchemeKeyTokens.Primary
     val PressedOutlineColor = ColorSchemeKeyTokens.Outline
     val PressedStateLayerColor = ColorSchemeKeyTokens.Primary
-    val BackgroundContainerColor = ColorSchemeKeyTokens.Surface
     val DisabledIconColor = ColorSchemeKeyTokens.OnSurface
     const val DisabledIconOpacity = 0.38f
     val FocusIconColor = ColorSchemeKeyTokens.Primary
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/OutlinedCardTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/OutlinedCardTokens.kt
index 217b545..cf531a3 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/OutlinedCardTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/OutlinedCardTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2021 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.
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -22,7 +23,7 @@
 internal object OutlinedCardTokens {
     val ContainerColor = ColorSchemeKeyTokens.Surface
     val ContainerElevation = ElevationTokens.Level0
-    val ContainerShape = ShapeTokens.Medium
+    val ContainerShape = ShapeTokens.CornerMedium
     val DraggedContainerElevation = ElevationTokens.Level3
     val DraggedOutlineColor = ColorSchemeKeyTokens.Outline
     val DraggedStateLayerColor = ColorSchemeKeyTokens.OnSurface
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/PaletteTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/PaletteTokens.kt
index c0cc9bd..658dddd3 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/PaletteTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/PaletteTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/PrimaryNavigationTabTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/PrimaryNavigationTabTokens.kt
new file mode 100644
index 0000000..a4771ef
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/PrimaryNavigationTabTokens.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 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.
+ */
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.compose.material3.tokens
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.ui.unit.dp
+
+internal object PrimaryNavigationTabTokens {
+    val ActiveIndicatorColor = ColorSchemeKeyTokens.Primary
+    val ActiveIndicatorHeight = 3.0.dp
+    val ActiveIndicatorShape = RoundedCornerShape(3.0.dp)
+    val ContainerColor = ColorSchemeKeyTokens.Surface
+    val ContainerElevation = ElevationTokens.Level0
+    val ContainerHeight = 48.0.dp
+    val DividerColor = ColorSchemeKeyTokens.SurfaceVariant
+    val DividerHeight = 1.0.dp
+    val ActiveFocusIconColor = ColorSchemeKeyTokens.Primary
+    val ActiveHoverIconColor = ColorSchemeKeyTokens.Primary
+    val ActiveIconColor = ColorSchemeKeyTokens.Primary
+    val ActivePressedIconColor = ColorSchemeKeyTokens.Primary
+    val IconAndLabelTextContainerHeight = 64.0.dp
+    val IconSize = 24.0.dp
+    val InactiveFocusIconColor = ColorSchemeKeyTokens.OnSurface
+    val InactiveHoverIconColor = ColorSchemeKeyTokens.OnSurface
+    val InactiveIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val InactivePressedIconColor = ColorSchemeKeyTokens.OnSurface
+    val ActiveFocusLabelTextColor = ColorSchemeKeyTokens.Primary
+    val ActiveHoverLabelTextColor = ColorSchemeKeyTokens.Primary
+    val ActiveLabelTextColor = ColorSchemeKeyTokens.Primary
+    val ActivePressedLabelTextColor = ColorSchemeKeyTokens.Primary
+    val InactiveFocusLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val InactiveHoverLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val InactiveLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val InactivePressedLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val LabelTextFont = TypographyKeyTokens.TitleSmall
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/RadioButtonTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/RadioButtonTokens.kt
index 04fb1d1..33066be 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/RadioButtonTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/RadioButtonTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ShapeTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ShapeTokens.kt
index f536558..c0034a0 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ShapeTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/ShapeTokens.kt
@@ -13,16 +13,48 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
+import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
-// TODO(b/209589029): Remove once the shape subsystem is in place.
 internal object ShapeTokens {
-    val Large = RoundedCornerShape(8.0.dp)
-    val Medium = RoundedCornerShape(8.0.dp)
-    val Small = RoundedCornerShape(4.0.dp)
+    val CornerExtraLarge = RoundedCornerShape(28.0.dp)
+    val CornerExtraLargeTop =
+        RoundedCornerShape(
+            topStart = 28.0.dp,
+            topEnd = 28.0.dp,
+            bottomEnd = 0.0.dp,
+            bottomStart = 0.0.dp
+        )
+    val CornerExtraSmall = RoundedCornerShape(4.0.dp)
+    val CornerExtraSmallTop = RoundedCornerShape(
+        topStart = 4.0.dp,
+        topEnd = 4.0.dp,
+        bottomEnd = 0.0.dp,
+        bottomStart = 0.0.dp
+    )
+    val CornerFull = CircleShape
+    val CornerLarge = RoundedCornerShape(16.0.dp)
+    val CornerLargeEnd =
+        RoundedCornerShape(
+            topStart = 0.0.dp,
+            topEnd = 16.0.dp,
+            bottomEnd = 16.0.dp,
+            bottomStart = 0.0.dp
+        )
+    val CornerLargeTop =
+        RoundedCornerShape(
+            topStart = 16.0.dp,
+            topEnd = 16.0.dp,
+            bottomEnd = 0.0.dp,
+            bottomStart = 0.0.dp
+        )
+    val CornerMedium = RoundedCornerShape(12.0.dp)
+    val CornerNone = RoundedCornerShape(0.0.dp)
+    val CornerSmall = RoundedCornerShape(8.0.dp)
 }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SnackbarTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SnackbarTokens.kt
index 5fdfb41..6652b9f 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SnackbarTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SnackbarTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -30,8 +31,14 @@
     val ActionPressedStateLayerColor = ColorSchemeKeyTokens.InversePrimary
     val ContainerColor = ColorSchemeKeyTokens.InverseSurface
     val ContainerElevation = ElevationTokens.Level3
-    val ContainerShape = ShapeTokens.Small
+    val ContainerShape = ShapeTokens.CornerExtraSmall
     val IconColor = ColorSchemeKeyTokens.InverseOnSurface
+    val FocusIconColor = ColorSchemeKeyTokens.InverseOnSurface
+    val IconFocusStateLayerColor = ColorSchemeKeyTokens.InverseOnSurface
+    val HoverIconColor = ColorSchemeKeyTokens.InverseOnSurface
+    val IconHoverStateLayerColor = ColorSchemeKeyTokens.InverseOnSurface
+    val PressedIconColor = ColorSchemeKeyTokens.InverseOnSurface
+    val IconPressedStateLayerColor = ColorSchemeKeyTokens.InverseOnSurface
     val IconSize = 24.0.dp
     val SupportingTextColor = ColorSchemeKeyTokens.InverseOnSurface
     val SupportingTextFont = TypographyKeyTokens.BodyMedium
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/StateTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/StateTokens.kt
index 497c18c..1a635be 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/StateTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/StateTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -22,4 +23,4 @@
     const val FocusStateLayerOpacity = 0.12f
     const val HoverStateLayerOpacity = 0.08f
     const val PressedStateLayerOpacity = 0.12f
-}
\ No newline at end of file
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TextButtonTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TextButtonTokens.kt
index 7e6f215..f9da716 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TextButtonTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TextButtonTokens.kt
@@ -13,17 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object TextButtonTokens {
-    val ContainerElevation = ElevationTokens.Level0
     val ContainerHeight = 40.0.dp
-    val ContainerShape = RoundedCornerShape(20.0.dp)
+    val ContainerShape = ShapeTokens.CornerFull
+    val DisabledContainerColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledContainerOpacity = 0.12f
     val DisabledLabelTextColor = ColorSchemeKeyTokens.OnSurface
     const val DisabledLabelTextOpacity = 0.38f
     val FocusLabelTextColor = ColorSchemeKeyTokens.Primary
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarLargeTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarLargeTokens.kt
index 55ea285..37003e1 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarLargeTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarLargeTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -23,6 +24,7 @@
     val ContainerColor = ColorSchemeKeyTokens.Surface
     val ContainerElevation = ElevationTokens.Level0
     val ContainerHeight = 152.0.dp
+    val ContainerShape = ShapeTokens.CornerNone
     val HeadlineColor = ColorSchemeKeyTokens.OnSurface
     val HeadlineFont = TypographyKeyTokens.HeadlineMedium
     val LeadingIconColor = ColorSchemeKeyTokens.OnSurface
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarMediumTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarMediumTokens.kt
index e53af13..11eb2a4 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarMediumTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarMediumTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -23,6 +24,7 @@
     val ContainerColor = ColorSchemeKeyTokens.Surface
     val ContainerElevation = ElevationTokens.Level0
     val ContainerHeight = 112.0.dp
+    val ContainerShape = ShapeTokens.CornerNone
     val HeadlineColor = ColorSchemeKeyTokens.OnSurface
     val HeadlineFont = TypographyKeyTokens.HeadlineSmall
     val LeadingIconColor = ColorSchemeKeyTokens.OnSurface
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarSmallCenteredTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarSmallCenteredTokens.kt
index 0ce48b3..51f6f7b 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarSmallCenteredTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarSmallCenteredTokens.kt
@@ -13,19 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
-import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.ui.unit.dp
 
 internal object TopAppBarSmallCenteredTokens {
-    val AvatarShape = RoundedCornerShape(15.0.dp)
+    val AvatarShape = ShapeTokens.CornerFull
     val AvatarSize = 30.0.dp
     val ContainerColor = ColorSchemeKeyTokens.Surface
     val ContainerElevation = ElevationTokens.Level0
     val ContainerHeight = 64.0.dp
+    val ContainerShape = ShapeTokens.CornerNone
     val HeadlineColor = ColorSchemeKeyTokens.OnSurface
     val HeadlineFont = TypographyKeyTokens.TitleLarge
     val LeadingIconColor = ColorSchemeKeyTokens.OnSurface
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarSmallTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarSmallTokens.kt
index 09a5cf4..0119f66 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarSmallTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TopAppBarSmallTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -23,6 +24,7 @@
     val ContainerColor = ColorSchemeKeyTokens.Surface
     val ContainerElevation = ElevationTokens.Level0
     val ContainerHeight = 64.0.dp
+    val ContainerShape = ShapeTokens.CornerNone
     val HeadlineColor = ColorSchemeKeyTokens.OnSurface
     val HeadlineFont = TypographyKeyTokens.TitleLarge
     val LeadingIconColor = ColorSchemeKeyTokens.OnSurface
@@ -30,4 +32,4 @@
     val OnScrollContainerElevation = ElevationTokens.Level2
     val TrailingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
     val TrailingIconSize = 24.0.dp
-}
\ No newline at end of file
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypeScaleTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypeScaleTokens.kt
index 250c436..079b793 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypeScaleTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypeScaleTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -20,77 +21,77 @@
 import androidx.compose.ui.unit.sp
 
 internal object TypeScaleTokens {
-    val BodyLargeFont = TypefaceTokens.PlainRegular
+    val BodyLargeFont = TypefaceTokens.Plain
     val BodyLargeLineHeight = 24.0.sp
     val BodyLargeSize = 16.sp
     val BodyLargeTracking = 0.5.sp
     val BodyLargeWeight = TypefaceTokens.WeightRegular
-    val BodyMediumFont = TypefaceTokens.PlainRegular
+    val BodyMediumFont = TypefaceTokens.Plain
     val BodyMediumLineHeight = 20.0.sp
     val BodyMediumSize = 14.sp
     val BodyMediumTracking = 0.2.sp
     val BodyMediumWeight = TypefaceTokens.WeightRegular
-    val BodySmallFont = TypefaceTokens.PlainRegular
+    val BodySmallFont = TypefaceTokens.Plain
     val BodySmallLineHeight = 16.0.sp
     val BodySmallSize = 12.sp
     val BodySmallTracking = 0.4.sp
     val BodySmallWeight = TypefaceTokens.WeightRegular
-    val DisplayLargeFont = TypefaceTokens.BrandRegular
+    val DisplayLargeFont = TypefaceTokens.Brand
     val DisplayLargeLineHeight = 64.0.sp
     val DisplayLargeSize = 57.sp
     val DisplayLargeTracking = -0.2.sp
     val DisplayLargeWeight = TypefaceTokens.WeightRegular
-    val DisplayMediumFont = TypefaceTokens.BrandRegular
+    val DisplayMediumFont = TypefaceTokens.Brand
     val DisplayMediumLineHeight = 52.0.sp
     val DisplayMediumSize = 45.sp
     val DisplayMediumTracking = 0.0.sp
     val DisplayMediumWeight = TypefaceTokens.WeightRegular
-    val DisplaySmallFont = TypefaceTokens.BrandRegular
+    val DisplaySmallFont = TypefaceTokens.Brand
     val DisplaySmallLineHeight = 44.0.sp
     val DisplaySmallSize = 36.sp
     val DisplaySmallTracking = 0.0.sp
     val DisplaySmallWeight = TypefaceTokens.WeightRegular
-    val HeadlineLargeFont = TypefaceTokens.BrandRegular
+    val HeadlineLargeFont = TypefaceTokens.Brand
     val HeadlineLargeLineHeight = 40.0.sp
     val HeadlineLargeSize = 32.sp
     val HeadlineLargeTracking = 0.0.sp
     val HeadlineLargeWeight = TypefaceTokens.WeightRegular
-    val HeadlineMediumFont = TypefaceTokens.BrandRegular
+    val HeadlineMediumFont = TypefaceTokens.Brand
     val HeadlineMediumLineHeight = 36.0.sp
     val HeadlineMediumSize = 28.sp
     val HeadlineMediumTracking = 0.0.sp
     val HeadlineMediumWeight = TypefaceTokens.WeightRegular
-    val HeadlineSmallFont = TypefaceTokens.BrandRegular
+    val HeadlineSmallFont = TypefaceTokens.Brand
     val HeadlineSmallLineHeight = 32.0.sp
     val HeadlineSmallSize = 24.sp
     val HeadlineSmallTracking = 0.0.sp
     val HeadlineSmallWeight = TypefaceTokens.WeightRegular
-    val LabelLargeFont = TypefaceTokens.PlainMedium
+    val LabelLargeFont = TypefaceTokens.Plain
     val LabelLargeLineHeight = 20.0.sp
     val LabelLargeSize = 14.sp
     val LabelLargeTracking = 0.1.sp
     val LabelLargeWeight = TypefaceTokens.WeightMedium
-    val LabelMediumFont = TypefaceTokens.PlainMedium
+    val LabelMediumFont = TypefaceTokens.Plain
     val LabelMediumLineHeight = 16.0.sp
     val LabelMediumSize = 12.sp
     val LabelMediumTracking = 0.5.sp
     val LabelMediumWeight = TypefaceTokens.WeightMedium
-    val LabelSmallFont = TypefaceTokens.PlainMedium
+    val LabelSmallFont = TypefaceTokens.Plain
     val LabelSmallLineHeight = 16.0.sp
     val LabelSmallSize = 11.sp
     val LabelSmallTracking = 0.5.sp
     val LabelSmallWeight = TypefaceTokens.WeightMedium
-    val TitleLargeFont = TypefaceTokens.BrandRegular
+    val TitleLargeFont = TypefaceTokens.Brand
     val TitleLargeLineHeight = 28.0.sp
     val TitleLargeSize = 22.sp
     val TitleLargeTracking = 0.0.sp
     val TitleLargeWeight = TypefaceTokens.WeightRegular
-    val TitleMediumFont = TypefaceTokens.PlainMedium
+    val TitleMediumFont = TypefaceTokens.Plain
     val TitleMediumLineHeight = 24.0.sp
     val TitleMediumSize = 16.sp
     val TitleMediumTracking = 0.2.sp
     val TitleMediumWeight = TypefaceTokens.WeightMedium
-    val TitleSmallFont = TypefaceTokens.PlainMedium
+    val TitleSmallFont = TypefaceTokens.Plain
     val TitleSmallLineHeight = 20.0.sp
     val TitleSmallSize = 14.sp
     val TitleSmallTracking = 0.1.sp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypefaceTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypefaceTokens.kt
index 50fb111..f84f714 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypefaceTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypefaceTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -21,11 +22,9 @@
 import androidx.compose.ui.text.font.FontWeight
 
 internal object TypefaceTokens {
-    val BrandMedium = FontFamily.SansSerif
-    val BrandRegular = FontFamily.SansSerif
-    val PlainMedium = FontFamily.SansSerif
-    val PlainRegular = FontFamily.SansSerif
+    val Brand = FontFamily.SansSerif
+    val Plain = FontFamily.SansSerif
     val WeightBold = FontWeight.Bold
     val WeightMedium = FontWeight.Medium
     val WeightRegular = FontWeight.Normal
-}
\ No newline at end of file
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypographyKeyTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypographyKeyTokens.kt
index a478348..4b99c2d 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypographyKeyTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypographyKeyTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypographyTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypographyTokens.kt
index e7829ae..3214a45 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypographyTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/TypographyTokens.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_76
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
diff --git a/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RememberSaveableTest.kt b/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RememberSaveableTest.kt
index 033b693..1d15b6c 100644
--- a/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RememberSaveableTest.kt
+++ b/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RememberSaveableTest.kt
@@ -410,6 +410,44 @@
             assertThat(composedValue).isEqualTo(1)
         }
     }
+
+    @Test
+    fun changingInputIsNotAffectingOrderOfRestoration() {
+        var counter = 0
+        var input by mutableStateOf(0)
+        var withInput: Int? = null
+        var withoutInput: String? = null
+
+        restorationTester.setContent {
+            withInput = rememberSaveable(input) { counter++ }
+            withoutInput = rememberSaveable { (counter++).toString() }
+        }
+
+        rule.runOnIdle {
+            assertThat(withInput).isNotNull()
+            withInput = null
+            input++
+        }
+
+        var expectedWithInput: Int? = null
+        var expectedWithoutInput: String? = null
+
+        rule.runOnIdle {
+            assertThat(withInput).isNotNull()
+            assertThat(withoutInput).isNotNull()
+            expectedWithInput = withInput
+            expectedWithoutInput = withoutInput
+            withInput = null
+            withoutInput = null
+        }
+
+        restorationTester.emulateSavedInstanceStateRestore()
+
+        rule.runOnIdle {
+            assertThat(withInput).isEqualTo(expectedWithInput)
+            assertThat(withoutInput).isEqualTo(expectedWithoutInput)
+        }
+    }
 }
 
 @Composable
diff --git a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt
index 44c0b1f..68f4cee 100644
--- a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt
+++ b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt
@@ -25,6 +25,7 @@
 import androidx.compose.runtime.neverEqualPolicy
 import androidx.compose.runtime.referentialEqualityPolicy
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.snapshots.SnapshotMutableState
 import androidx.compose.runtime.structuralEqualityPolicy
 
@@ -87,17 +88,19 @@
         restored ?: init()
     }
 
-    // save the latest passed saver object into a state object to be able to use it when we will
-    // be saving the value. keeping value in mutableStateOf() allows us to properly handle
-    // possible compose transactions cancellations
-    val saverHolder = remember { mutableStateOf(saver) }
-    saverHolder.value = saver
-
     // re-register if the registry or key has been changed
     if (registry != null) {
-        DisposableEffect(registry, finalKey, value) {
+        // we want to use the latest instances of saver and value in the valueProvider lambda
+        // without restarting DisposableEffect as it would cause re-registering the provider in
+        // the different order. so we use rememberUpdatedState.
+        val saverState = rememberUpdatedState(saver)
+        val valueState = rememberUpdatedState(value)
+
+        DisposableEffect(registry, finalKey) {
             val valueProvider = {
-                with(saverHolder.value) { SaverScope { registry.canBeSaved(it) }.save(value) }
+                with(saverState.value) {
+                    SaverScope { registry.canBeSaved(it) }.save(valueState.value)
+                }
             }
             registry.requireCanBeSaved(valueProvider())
             val entry = registry.registerProvider(finalKey, valueProvider)
diff --git a/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
index 7622f47..7883dfb 100644
--- a/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
@@ -16,12 +16,12 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.compose.runtime.benchmark">
+    package="androidx.compose.runtime.benchmark.test">
     <application>
         <!-- enable profiling by shell for non-intrusive profiling tools -->
         <profileable android:shell="true"/>
 
         <activity
-            android:name=".ComposeActivity"/>
+            android:name="androidx.compose.runtime.benchmark.ComposeActivity"/>
     </application>
 </manifest>
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
index 98a18e4..a677880 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
@@ -28,5 +28,5 @@
      * IMPORTANT: Whenever updating this value, please make sure to also update `versionTable` and
      * `minimumRuntimeVersionInt` in `VersionChecker.kt` of the compiler.
      */
-    const val version: Int = 6200
+    const val version: Int = 6300
 }
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
index e48c49a..b53796a 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
@@ -84,6 +84,7 @@
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.compose.ui.window.Popup
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth.assertThat
@@ -727,6 +728,7 @@
     }
     // WARNING: End formatted section
 
+    @FlakyTest(bugId = 217610030)
     @Test
     fun testLineNumbers() {
         // WARNING: The formatting of the lines below here affect test results.
diff --git a/compose/ui/ui-test-junit4/src/androidAndroidTest/AndroidManifest.xml b/compose/ui/ui-test-junit4/src/androidAndroidTest/AndroidManifest.xml
index b0777ee..b5891be 100644
--- a/compose/ui/ui-test-junit4/src/androidAndroidTest/AndroidManifest.xml
+++ b/compose/ui/ui-test-junit4/src/androidAndroidTest/AndroidManifest.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.compose.ui.test.junit4">
+    package="androidx.compose.ui.test.junit4.test">
     <application>
         <activity android:name="androidx.compose.ui.test.junit4.CustomActivity"
             android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen" />
@@ -26,8 +26,8 @@
         <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesFirstDrawTest$Activity1" />
         <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesFirstDrawTest$Activity2" />
         <activity android:name="androidx.compose.ui.test.junit4.LateSetContentTest$Activity" />
-        <activity android:name=".MultipleActivitiesWithoutComposeTest$Activity1" />
-        <activity android:name=".MultipleActivitiesWithoutComposeTest$Activity2" />
-        <activity android:name=".MultipleActivitiesWithoutComposeTest$Activity3" />
+        <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesWithoutComposeTest$Activity1" />
+        <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesWithoutComposeTest$Activity2" />
+        <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesWithoutComposeTest$Activity3" />
     </application>
 </manifest>
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index 9e5c9ce..6249ecb 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -922,7 +922,7 @@
 
   public interface PlatformTextInputService {
     method public void hideSoftwareKeyboard();
-    method public void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
+    method @Deprecated public default void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
     method public void showSoftwareKeyboard();
     method public void startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.ImeOptions imeOptions, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditCommand>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
     method public void stopInput();
@@ -999,7 +999,7 @@
     method public void dispose();
     method public boolean hideSoftwareKeyboard();
     method public boolean isOpen();
-    method public boolean notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
+    method @Deprecated public boolean notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
     method public boolean showSoftwareKeyboard();
     method public boolean updateState(androidx.compose.ui.text.input.TextFieldValue? oldValue, androidx.compose.ui.text.input.TextFieldValue newValue);
     property public final boolean isOpen;
diff --git a/compose/ui/ui-text/api/public_plus_experimental_current.txt b/compose/ui/ui-text/api/public_plus_experimental_current.txt
index 49abfc4..71868a4 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_current.txt
@@ -960,7 +960,7 @@
 
   public interface PlatformTextInputService {
     method public void hideSoftwareKeyboard();
-    method public void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
+    method @Deprecated public default void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
     method public void showSoftwareKeyboard();
     method public void startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.ImeOptions imeOptions, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditCommand>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
     method public void stopInput();
@@ -1037,7 +1037,7 @@
     method public void dispose();
     method public boolean hideSoftwareKeyboard();
     method public boolean isOpen();
-    method public boolean notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
+    method @Deprecated public boolean notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
     method public boolean showSoftwareKeyboard();
     method public boolean updateState(androidx.compose.ui.text.input.TextFieldValue? oldValue, androidx.compose.ui.text.input.TextFieldValue newValue);
     property public final boolean isOpen;
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index 9e5c9ce..6249ecb 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -922,7 +922,7 @@
 
   public interface PlatformTextInputService {
     method public void hideSoftwareKeyboard();
-    method public void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
+    method @Deprecated public default void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
     method public void showSoftwareKeyboard();
     method public void startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.ImeOptions imeOptions, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditCommand>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
     method public void stopInput();
@@ -999,7 +999,7 @@
     method public void dispose();
     method public boolean hideSoftwareKeyboard();
     method public boolean isOpen();
-    method public boolean notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
+    method @Deprecated public boolean notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
     method public boolean showSoftwareKeyboard();
     method public boolean updateState(androidx.compose.ui.text.input.TextFieldValue? oldValue, androidx.compose.ui.text.input.TextFieldValue newValue);
     property public final boolean isOpen;
diff --git a/compose/ui/ui-text/build.gradle b/compose/ui/ui-text/build.gradle
index cebbec7..ba793b3 100644
--- a/compose/ui/ui-text/build.gradle
+++ b/compose/ui/ui-text/build.gradle
@@ -45,6 +45,7 @@
         implementation(project(":compose:ui:ui-util"))
         implementation(libs.kotlinStdlib)
         implementation("androidx.core:core:1.5.0")
+        implementation('androidx.collection:collection:1.0.0')
 
         testImplementation(project(":compose:ui:ui-test-font"))
         testImplementation(libs.testRules)
@@ -122,6 +123,7 @@
             androidMain.dependencies {
                 api("androidx.annotation:annotation:1.1.0")
                 implementation("androidx.core:core:1.5.0")
+                implementation('androidx.collection:collection:1.0.0')
             }
 
             androidMain.kotlin.srcDirs("../../../text/text/src/main/java")
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt
index e145592..79cbf37 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt
@@ -147,14 +147,8 @@
         }
     }
 
-    /**
-     * Notify the focused rectangle to the system.
-     *
-     * If the session is not open, no action will be performed.
-     *
-     * @param rect the rectangle that describes the boundaries on the screen that requires focus
-     * @return false if this session expired and no action was performed
-     */
+    @Suppress("DeprecatedCallableAddReplaceWith", "DEPRECATION")
+    @Deprecated("This method should not be called, used BringIntoViewRequester instead.")
     fun notifyFocusedRect(rect: Rect): Boolean = ensureOpenSession {
         platformTextInputService.notifyFocusedRect(rect)
     }
@@ -261,10 +255,7 @@
      */
     fun updateState(oldValue: TextFieldValue?, newValue: TextFieldValue)
 
-    /**
-     * Notify the focused rectangle to the system.
-     *
-     * @see TextInputSession.notifyFocusedRect
-     */
-    fun notifyFocusedRect(rect: Rect)
+    @Deprecated("This method should not be called, used BringIntoViewRequester instead.")
+    fun notifyFocusedRect(rect: Rect) {
+    }
 }
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt
index 92c1bb1..10b25c6 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt
@@ -34,6 +34,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
+@Suppress("DEPRECATION")
 @RunWith(JUnit4::class)
 class TextInputServiceTest {
 
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index 0ddeaae..2ea6eba 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -57,6 +57,8 @@
         implementation("androidx.autofill:autofill:1.0.0")
         implementation(libs.kotlinCoroutinesAndroid)
 
+        implementation("androidx.core:core:1.5.0")
+        implementation('androidx.collection:collection:1.0.0')
         implementation("androidx.savedstate:savedstate:1.1.0")
         implementation("androidx.lifecycle:lifecycle-common-java8:2.3.0")
         implementation("androidx.lifecycle:lifecycle-runtime:2.3.0")
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CustomFocusTraversalTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CustomFocusTraversalTest.kt
index 9e2ccd6..91fdc26 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CustomFocusTraversalTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CustomFocusTraversalTest.kt
@@ -89,7 +89,9 @@
 
         // Act.
         if (moveFocusProgrammatically) {
-            focusManager.moveFocus(FocusDirection.Next)
+            rule.runOnIdle {
+                focusManager.moveFocus(FocusDirection.Next)
+            }
         } else {
             rule.onRoot().performKeyPress(KeyEvent(AndroidKeyEvent(KeyDown, Tab.nativeKeyCode)))
         }
@@ -136,7 +138,9 @@
 
         // Act.
         if (moveFocusProgrammatically) {
-            focusManager.moveFocus(FocusDirection.Previous)
+            rule.runOnIdle {
+                focusManager.moveFocus(FocusDirection.Previous)
+            }
         } else {
             val nativeEvent = AndroidKeyEvent(0L, 0L, KeyDown, Tab.nativeKeyCode, 0, META_SHIFT_ON)
             rule.onRoot().performKeyPress(KeyEvent(nativeEvent))
@@ -184,7 +188,9 @@
 
         // Act.
         if (moveFocusProgrammatically) {
-            focusManager.moveFocus(FocusDirection.Up)
+            rule.runOnIdle {
+                focusManager.moveFocus(FocusDirection.Up)
+            }
         } else {
             val nativeKeyEvent = AndroidKeyEvent(KeyDown, DirectionUp.nativeKeyCode)
             rule.onRoot().performKeyPress(KeyEvent(nativeKeyEvent))
@@ -232,7 +238,9 @@
 
         // Act.
         if (moveFocusProgrammatically) {
-            focusManager.moveFocus(FocusDirection.Down)
+            rule.runOnIdle {
+                focusManager.moveFocus(FocusDirection.Down)
+            }
         } else {
             val nativeKeyEvent = AndroidKeyEvent(KeyDown, DirectionDown.nativeKeyCode)
             rule.onRoot().performKeyPress(KeyEvent(nativeKeyEvent))
@@ -280,7 +288,9 @@
 
         // Act.
         if (moveFocusProgrammatically) {
-            focusManager.moveFocus(FocusDirection.Left)
+            rule.runOnIdle {
+                focusManager.moveFocus(FocusDirection.Left)
+            }
         } else {
             val nativeKeyEvent = AndroidKeyEvent(KeyDown, DirectionLeft.nativeKeyCode)
             rule.onRoot().performKeyPress(KeyEvent(nativeKeyEvent))
@@ -328,7 +338,9 @@
 
         // Act.
         if (moveFocusProgrammatically) {
-            focusManager.moveFocus(FocusDirection.Right)
+            rule.runOnIdle {
+                focusManager.moveFocus(FocusDirection.Right)
+            }
         } else {
             val nativeKeyEvent = AndroidKeyEvent(KeyDown, DirectionRight.nativeKeyCode)
             rule.onRoot().performKeyPress(KeyEvent(nativeKeyEvent))
@@ -378,7 +390,9 @@
 
         // Act.
         if (moveFocusProgrammatically) {
-            focusManager.moveFocus(FocusDirection.Left)
+            rule.runOnIdle {
+                focusManager.moveFocus(FocusDirection.Left)
+            }
         } else {
             val nativeKeyEvent = AndroidKeyEvent(KeyDown, DirectionLeft.nativeKeyCode)
             rule.onRoot().performKeyPress(KeyEvent(nativeKeyEvent))
@@ -428,7 +442,9 @@
 
         // Act.
         if (moveFocusProgrammatically) {
-            focusManager.moveFocus(FocusDirection.Right)
+            rule.runOnIdle {
+                focusManager.moveFocus(FocusDirection.Right)
+            }
         } else {
             val nativeKeyEvent = AndroidKeyEvent(KeyDown, DirectionRight.nativeKeyCode)
             rule.onRoot().performKeyPress(KeyEvent(nativeKeyEvent))
@@ -485,7 +501,9 @@
 
         // Act.
         if (moveFocusProgrammatically) {
-            focusManager.moveFocus(FocusDirection.Next)
+            rule.runOnIdle {
+                focusManager.moveFocus(FocusDirection.Next)
+            }
         } else {
             rule.onRoot().performKeyPress(KeyEvent(AndroidKeyEvent(KeyDown, Tab.nativeKeyCode)))
         }
@@ -535,7 +553,9 @@
 
         // Act.
         if (moveFocusProgrammatically) {
-            focusManager.moveFocus(FocusDirection.Next)
+            rule.runOnIdle {
+                focusManager.moveFocus(FocusDirection.Next)
+            }
         } else {
             rule.onRoot().performKeyPress(KeyEvent(AndroidKeyEvent(KeyDown, Tab.nativeKeyCode)))
         }
@@ -584,7 +604,9 @@
 
         // Act.
         if (moveFocusProgrammatically) {
-            focusManager.moveFocus(FocusDirection.Next)
+            rule.runOnIdle {
+                focusManager.moveFocus(FocusDirection.Next)
+            }
         } else {
             rule.onRoot().performKeyPress(KeyEvent(AndroidKeyEvent(KeyDown, Tab.nativeKeyCode)))
         }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusViewInteropTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusViewInteropTest.kt
new file mode 100644
index 0000000..47e1944
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusViewInteropTest.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2022 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.ui.focus
+
+import android.graphics.Rect as AndroidRect
+import android.view.View
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class FocusViewInteropTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun getFocusedRect_reportsFocusBounds_whenFocused() {
+        val focusRequester = FocusRequester()
+        var hasFocus = false
+        lateinit var view: View
+        rule.setContent {
+            view = LocalView.current
+            CompositionLocalProvider(LocalDensity provides Density(density = 1f)) {
+                Box(
+                    Modifier
+                        .size(90.dp, 100.dp)
+                        .wrapContentSize(align = Alignment.TopStart)
+                        .size(10.dp, 20.dp)
+                        .offset(30.dp, 40.dp)
+                        .onFocusChanged {
+                            if (it.isFocused) {
+                                hasFocus = true
+                            }
+                        }
+                        .focusRequester(focusRequester)
+                        .focusable()
+                )
+            }
+        }
+        rule.runOnIdle {
+            focusRequester.requestFocus()
+        }
+
+        rule.waitUntil { hasFocus }
+
+        assertThat(view.getFocusedRect()).isEqualTo(IntRect(30, 40, 40, 60))
+    }
+
+    @Test
+    fun getFocusedRect_reportsEntireView_whenNoFocus() {
+        lateinit var view: View
+        rule.setContent {
+            view = LocalView.current
+            CompositionLocalProvider(LocalDensity provides Density(density = 1f)) {
+                Box(
+                    Modifier
+                        .size(90.dp, 100.dp)
+                        .wrapContentSize(align = Alignment.TopStart)
+                        .size(10.dp, 20.dp)
+                        .offset(30.dp, 40.dp)
+                        .focusable()
+                )
+            }
+        }
+
+        assertThat(view.getFocusedRect()).isEqualTo(
+            IntRect(0, 0, 90, 100)
+        )
+    }
+
+    private fun View.getFocusedRect() = AndroidRect().run {
+        rule.runOnIdle {
+            getFocusedRect(this)
+        }
+        IntRect(left, top, right, bottom)
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusTraversalThreeItemsTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusTraversalThreeItemsTest.kt
index e74a88a..414f89d 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusTraversalThreeItemsTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusTraversalThreeItemsTest.kt
@@ -115,7 +115,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -184,7 +186,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -258,7 +262,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -332,7 +338,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -402,7 +410,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -472,7 +482,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -542,7 +554,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -612,7 +626,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -682,7 +698,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -752,7 +770,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -821,7 +841,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -891,7 +913,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -961,7 +985,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1031,7 +1057,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1101,7 +1129,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1170,7 +1200,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1240,7 +1272,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1310,7 +1344,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1381,7 +1417,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1451,7 +1489,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1521,7 +1561,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1592,7 +1634,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1660,7 +1704,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1729,7 +1775,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1798,7 +1846,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1867,7 +1917,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1934,7 +1986,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2001,7 +2055,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2070,7 +2126,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2140,7 +2198,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2210,7 +2270,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2279,7 +2341,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2344,7 +2408,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2412,7 +2478,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2477,7 +2545,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2544,7 +2614,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2613,7 +2685,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2683,7 +2757,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2753,7 +2829,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2821,7 +2899,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2885,7 +2965,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2948,7 +3030,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3011,7 +3095,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3076,7 +3162,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3145,7 +3233,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3212,7 +3302,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3281,7 +3373,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3348,7 +3442,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3417,7 +3513,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3484,7 +3582,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3554,7 +3654,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3632,7 +3734,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3711,7 +3815,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3792,7 +3898,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3872,7 +3980,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3950,7 +4060,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4031,7 +4143,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4111,7 +4225,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4189,7 +4305,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4260,7 +4378,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4329,7 +4449,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4398,7 +4520,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4468,7 +4592,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4538,7 +4664,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4607,7 +4735,9 @@
             }
         }
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4676,7 +4806,9 @@
             }
         }
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4745,7 +4877,9 @@
             }
         }
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4808,7 +4942,9 @@
             }
         }
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4881,7 +5017,9 @@
             }
         }
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4954,7 +5092,9 @@
             }
         }
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -5027,7 +5167,9 @@
             }
         }
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusTraversalTwoItemsTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusTraversalTwoItemsTest.kt
index 00eed69..52425ca 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusTraversalTwoItemsTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusTraversalTwoItemsTest.kt
@@ -77,7 +77,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -113,7 +115,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -149,7 +153,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -185,7 +191,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -221,7 +229,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -257,7 +267,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -293,7 +305,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -329,7 +343,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -364,7 +380,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -398,7 +416,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -432,7 +452,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -467,7 +489,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -502,7 +526,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -537,7 +563,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -572,7 +600,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -605,7 +635,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -639,7 +671,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -673,7 +707,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -708,7 +744,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -744,7 +782,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -780,7 +820,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -816,7 +858,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -852,7 +896,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -888,7 +934,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -924,7 +972,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -960,7 +1010,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -996,7 +1048,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1032,7 +1086,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1068,7 +1124,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1104,7 +1162,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1140,7 +1200,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1175,7 +1237,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1209,7 +1273,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1243,7 +1309,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1276,7 +1344,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1311,7 +1381,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1346,7 +1418,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1381,7 +1455,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1416,7 +1492,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1450,7 +1528,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1484,7 +1564,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1519,7 +1601,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1555,7 +1639,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1591,7 +1677,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1627,7 +1715,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1663,7 +1753,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1698,7 +1790,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1733,7 +1827,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1768,7 +1864,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1803,7 +1901,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1838,7 +1938,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1873,7 +1975,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1908,7 +2012,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1942,7 +2048,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -1976,7 +2084,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2011,7 +2121,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2046,7 +2158,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2081,7 +2195,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2116,7 +2232,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2149,7 +2267,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2183,7 +2303,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2217,7 +2339,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2252,7 +2376,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2287,7 +2413,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2322,7 +2450,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2357,7 +2487,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2392,7 +2524,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2427,7 +2561,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2462,7 +2598,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2497,7 +2635,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2532,7 +2672,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2567,7 +2709,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2601,7 +2745,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2635,7 +2781,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2668,7 +2816,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2703,7 +2853,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2738,7 +2890,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2773,7 +2927,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2808,7 +2964,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2842,7 +3000,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2876,7 +3036,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2911,7 +3073,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2946,7 +3110,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -2981,7 +3147,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3017,7 +3185,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3053,7 +3223,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3089,7 +3261,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3125,7 +3299,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3161,7 +3337,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3195,7 +3373,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3229,7 +3409,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3264,7 +3446,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3299,7 +3483,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3334,7 +3520,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3369,7 +3557,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3402,7 +3592,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3436,7 +3628,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3470,7 +3664,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3506,7 +3702,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3542,7 +3740,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3578,7 +3778,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3614,7 +3816,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3650,7 +3854,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3686,7 +3892,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3720,7 +3928,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3754,7 +3964,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3787,7 +3999,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3822,7 +4036,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3857,7 +4073,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3892,7 +4110,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3927,7 +4147,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3961,7 +4183,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -3995,7 +4219,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4031,7 +4257,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4066,7 +4294,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4092,7 +4322,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4118,7 +4350,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4144,7 +4378,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4170,7 +4406,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4196,7 +4434,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4222,7 +4462,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4248,7 +4490,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4275,7 +4519,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4301,7 +4547,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4327,7 +4575,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4353,7 +4603,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4379,7 +4631,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4405,7 +4659,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4431,7 +4687,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4457,7 +4715,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4483,7 +4743,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4510,7 +4772,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4575,7 +4839,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
@@ -4595,7 +4861,9 @@
         }
 
         // Act.
-        focusManager.moveFocus(focusDirection)
+        rule.runOnIdle {
+            focusManager.moveFocus(focusDirection)
+        }
 
         // Assert.
         rule.runOnIdle {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
index 8637ce5..dc297f4 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
@@ -35,6 +35,8 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.saveable.rememberSaveableStateHolder
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.staticCompositionLocalOf
 import androidx.compose.ui.Modifier
@@ -55,6 +57,7 @@
 import androidx.compose.ui.test.assertPositionInRootIsEqualTo
 import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.StateRestorationTester
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Constraints
@@ -67,14 +70,14 @@
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThrows
 import org.junit.Assert.assertTrue
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-import org.junit.Assert.assertThrows
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
@@ -1756,6 +1759,44 @@
         }
     }
 
+    @Test
+    fun stateIsRestoredWhenGoBackToScreen1WithSubcomposition() {
+        val restorationTester = StateRestorationTester(rule)
+
+        var increment = 0
+        var screen by mutableStateOf(Screens.Screen1)
+        var restorableNumberOnScreen1 = -1
+        restorationTester.setContent {
+            val holder = rememberSaveableStateHolder()
+            holder.SaveableStateProvider(screen) {
+                if (screen == Screens.Screen1) {
+                    SubcomposeLayout {
+                        subcompose(Unit) {
+                            restorableNumberOnScreen1 = rememberSaveable { increment++ }
+                        }
+                        layout(10, 10) {}
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(restorableNumberOnScreen1).isEqualTo(0)
+            screen = Screens.Screen2
+        }
+
+        // wait for the screen switch to apply
+        rule.runOnIdle {
+            restorableNumberOnScreen1 = -1
+            // switch back to screen1
+            screen = Screens.Screen1
+        }
+
+        rule.runOnIdle {
+            assertThat(restorableNumberOnScreen1).isEqualTo(0)
+        }
+    }
+
     private fun composeItems(
         state: SubcomposeLayoutState,
         items: MutableState<List<Int>>
@@ -1804,4 +1845,9 @@
             placeable.place(0, 0)
         }
     }
+}
+
+private enum class Screens {
+    Screen1,
+    Screen2,
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
index d8bcdc2..b68ed8e 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.platform
 
+import android.view.KeyEvent as AndroidKeyEvent
 import android.annotation.SuppressLint
 import android.content.Context
 import android.content.res.Configuration
@@ -113,6 +114,7 @@
 import androidx.compose.ui.input.rotary.RotaryScrollEvent
 import androidx.compose.ui.input.rotary.onRotaryScrollEvent
 import androidx.compose.ui.layout.RootMeasurePolicy
+import androidx.compose.ui.layout.boundsInRoot
 import androidx.compose.ui.node.InternalCoreApi
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.node.LayoutNode.UsageByParent
@@ -153,7 +155,7 @@
 import androidx.savedstate.SavedStateRegistryOwner
 import androidx.savedstate.ViewTreeSavedStateRegistryOwner
 import java.lang.reflect.Method
-import android.view.KeyEvent as AndroidKeyEvent
+import kotlin.math.roundToInt
 
 @SuppressLint("ViewConstructor", "VisibleForTests")
 @OptIn(ExperimentalComposeUiApi::class)
@@ -525,6 +527,19 @@
         }
     }
 
+    /**
+     * Since this view has its own concept of internal focus, it needs to report that to the view
+     * system for accurate focus searching and so ViewRootImpl will scroll correctly.
+     */
+    override fun getFocusedRect(rect: Rect) {
+        _focusManager.getActiveFocusModifier()?.focusNode?.boundsInRoot()?.let {
+            rect.left = it.left.roundToInt()
+            rect.top = it.top.roundToInt()
+            rect.right = it.right.roundToInt()
+            rect.bottom = it.bottom.roundToInt()
+        } ?: super.getFocusedRect(rect)
+    }
+
     override fun onResume(owner: LifecycleOwner) {
         // Refresh in onResume in case the value has changed.
         @OptIn(InternalCoreApi::class)
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt
index 6a5dc7c..d46d49d 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt
@@ -16,11 +16,11 @@
 
 package androidx.compose.ui.text.input
 
+import android.graphics.Rect as AndroidRect
 import android.text.InputType
 import android.util.Log
 import android.view.KeyEvent
 import android.view.View
-import android.view.ViewTreeObserver
 import android.view.inputmethod.BaseInputConnection
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputConnection
@@ -73,12 +73,13 @@
         private set
     private var imeOptions = ImeOptions.Default
     private var ic: RecordingInputConnection? = null
+
     // used for sendKeyEvent delegation
     private val baseInputConnection by lazy(LazyThreadSafetyMode.NONE) {
         BaseInputConnection(view, false)
     }
 
-    private var focusedRect: android.graphics.Rect? = null
+    private var focusedRect: AndroidRect? = null
 
     /**
      * A channel that is used to debounce rapid operations such as showing/hiding the keyboard and
@@ -88,30 +89,10 @@
      */
     private val textInputCommandChannel = Channel<TextInputCommand>(Channel.UNLIMITED)
 
-    private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
-        // focusedRect is null if there is not ongoing text input session. So safe to request
-        // latest focused rectangle whenever global layout has changed.
-        focusedRect?.let {
-            // Notice that view.requestRectangleOnScreen may modify the input Rect, we have to
-            // create another Rect and then pass it.
-            view.requestRectangleOnScreen(android.graphics.Rect(it))
-        }
-    }
-
     internal constructor(view: View) : this(view, InputMethodManagerImpl(view.context))
 
     init {
         if (DEBUG) { Log.d(TAG, "$DEBUG_CLASS.create") }
-
-        view.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
-            override fun onViewDetachedFromWindow(v: View?) {
-                v?.rootView?.viewTreeObserver?.removeOnGlobalLayoutListener(layoutListener)
-            }
-
-            override fun onViewAttachedToWindow(v: View?) {
-                v?.rootView?.viewTreeObserver?.addOnGlobalLayoutListener(layoutListener)
-            }
-        })
     }
 
     /**
@@ -353,8 +334,9 @@
         }
     }
 
+    @Suppress("OverridingDeprecatedMember")
     override fun notifyFocusedRect(rect: Rect) {
-        focusedRect = android.graphics.Rect(
+        focusedRect = AndroidRect(
             rect.left.roundToInt(),
             rect.top.roundToInt(),
             rect.right.roundToInt(),
@@ -371,7 +353,7 @@
             focusedRect?.let {
                 // Notice that view.requestRectangleOnScreen may modify the input Rect, we have to
                 // create another Rect and then pass it.
-                view.requestRectangleOnScreen(android.graphics.Rect(it))
+                view.requestRectangleOnScreen(AndroidRect(it))
             }
         }
     }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
index dbd2034..d719cc7 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
@@ -21,10 +21,12 @@
 import androidx.compose.runtime.ComposeNode
 import androidx.compose.runtime.Composition
 import androidx.compose.runtime.CompositionContext
+import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.currentComposer
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCompositionContext
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.SubcomposeLayoutState.PrecomposedSlotHandle
 import androidx.compose.ui.materialize
@@ -119,6 +121,12 @@
             state.forceRecomposeChildren()
         }
     }
+    val stateHolder = rememberUpdatedState(state)
+    DisposableEffect(Unit) {
+        onDispose {
+            stateHolder.value.disposeCurrentNodes()
+        }
+    }
 }
 
 /**
@@ -211,6 +219,8 @@
 
     internal fun forceRecomposeChildren() = state.forceRecomposeChildren()
 
+    internal fun disposeCurrentNodes() = state.disposeCurrentNodes()
+
     /**
      * Instance of this interface is returned by [precompose] function.
      */
@@ -421,6 +431,8 @@
                     reusableCount++
                 } else {
                     ignoreRemeasureRequests {
+                        val nodeState = nodeToNodeState.remove(root.foldedChildren[i])!!
+                        nodeState.composition?.dispose()
                         root.removeAt(i, 1)
                     }
                 }
@@ -486,12 +498,6 @@
         }
     }
 
-    private fun disposeNode(node: LayoutNode) {
-        val nodeState = nodeToNodeState.remove(node)!!
-        nodeState.composition?.dispose()
-        slotIdToNode.remove(nodeState.slotId)
-    }
-
     fun createMeasurePolicy(
         block: SubcomposeMeasureScope.(Constraints) -> MeasureResult
     ): MeasurePolicy = object : LayoutNode.NoIntrinsicsMeasurePolicy(error = NoIntrinsicsMessage) {
@@ -601,12 +607,6 @@
         ignoreRemeasureRequests {
             root.insertAt(index, node)
         }
-        node.onDetach = {
-            disposeNode(node)
-            node.onAttach = {
-                throw IllegalStateException("Disposed node shouldn't be reattached")
-            }
-        }
     }
 
     private fun move(from: Int, to: Int, count: Int = 1) {
@@ -618,6 +618,14 @@
     private inline fun ignoreRemeasureRequests(block: () -> Unit) =
         root.ignoreRemeasureRequests(block)
 
+    fun disposeCurrentNodes() {
+        nodeToNodeState.values.forEach {
+            it.composition?.dispose()
+        }
+        nodeToNodeState.clear()
+        slotIdToNode.clear()
+    }
+
     private class NodeState(
         var slotId: Any?,
         var content: @Composable () -> Unit,
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/TestUtils.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/TestUtils.kt
index efa65e9..2d891af 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/TestUtils.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/TestUtils.kt
@@ -57,7 +57,8 @@
     modifiers: Int = 0
 ): Boolean {
     val event = KeyEvent(
-        focusOwner,
+        // if we would just use `focusOwner` then it will be null if the window is minimized
+        mostRecentFocusOwner,
         KeyEvent.KEY_PRESSED,
         0,
         modifiers,
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposeWindowTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposeWindowTest.kt
new file mode 100644
index 0000000..fa68749
--- /dev/null
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposeWindowTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 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.ui.awt
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.sendMouseEvent
+import androidx.compose.ui.window.runApplicationTest
+import com.google.common.truth.Truth.assertThat
+import java.awt.Dimension
+import java.awt.event.MouseEvent
+import org.junit.Test
+
+class ComposeWindowTest {
+    // bug https://github.com/JetBrains/compose-jb/issues/1448
+    @Test
+    fun `dispose window inside event handler`() = runApplicationTest {
+        var isClickHappened = false
+
+        val window = ComposeWindow()
+        window.isUndecorated = true
+        window.size = Dimension(200, 200)
+        window.setContent {
+            Box(modifier = Modifier.fillMaxSize().background(Color.Blue).clickable {
+                isClickHappened = true
+                window.dispose()
+            })
+        }
+
+        window.isVisible = true
+        awaitIdle()
+
+        window.sendMouseEvent(MouseEvent.MOUSE_PRESSED, x = 100, y = 50)
+        window.sendMouseEvent(MouseEvent.MOUSE_RELEASED, x = 100, y = 50)
+        awaitIdle()
+
+        assertThat(isClickHappened).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/ComposeScene.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/ComposeScene.skiko.kt
index 800469a..1cccfd0 100644
--- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/ComposeScene.skiko.kt
+++ b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/ComposeScene.skiko.kt
@@ -114,7 +114,7 @@
 
     private fun invalidateIfNeeded() {
         hasPendingDraws = frameClock.hasAwaiters || list.any(SkiaBasedOwner::needRender)
-        if (hasPendingDraws && !isInvalidationDisabled) {
+        if (hasPendingDraws && !isInvalidationDisabled && !isClosed) {
             invalidate()
         }
     }
diff --git a/core/core-splashscreen/samples/build.gradle b/core/core-splashscreen/samples/build.gradle
index 05baf9f..10d6bd4 100644
--- a/core/core-splashscreen/samples/build.gradle
+++ b/core/core-splashscreen/samples/build.gradle
@@ -38,6 +38,7 @@
     implementation project(":annotation:annotation")
     implementation(project(":core:core-splashscreen"))
     implementation(project(":core:core-ktx"))
+    implementation(project(":interpolator:interpolator"))
     compileOnly(project(":annotation:annotation-sampled"))
 }
 
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index e4a9051..cc3fedc 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -320,7 +320,7 @@
 \$CHECKOUT/prebuilts/androidx/external/org/jetbrains/kotlin/kotlin\-stdlib\-jdk[0-9]+/[0-9]+\.[0-9]+\.[0-9]+-?[\-A-Z0-9]*/kotlin\-stdlib\-jdk[0-9]+\-[0-9]+\.[0-9]+\.[0-9]+-?[\-A-Z0-9]*\.jar \(version [0-9]+\.[0-9]+-?[\-A-Z0-9]*\)
 # > Task :compose:ui:ui:processDebugAndroidTestManifest
 \$OUT_DIR/androidx/compose/ui/ui/build/intermediates/tmp/manifest/androidTest/debug/tempFile[0-9]+ProcessTestManifest[0-9]+\.xml Warning:
-Package name 'androidx\.compose\.ui\.test' used in: tempFile[0-9]+ProcessTestManifest[0-9]+\.xml, :compose:ui:ui\-test\.
+Namespace 'androidx\.compose\.ui\.test' used in: tempFile[0-9]+ProcessTestManifest[0-9]+\.xml, :compose:ui:ui\-test\.
 # > Task :buildSrc:build UP-TO-DATE
 See the profiling report at\: file\:\/\/\$OUT_DIR\/buildSrc\/build\/reports\/profile\/profile\-[0-9]+\-[0-9]+\-[0-9]+\-[0-9]+\-[0-9]+\-[0-9]+\.html
 A fine\-grained performance profile is available\: use the \-\-scan option\.
@@ -595,9 +595,9 @@
 # > Task :car:app:app:generateProtocolApi
 \- Task `:car:app:app:generateProtocolApi` of type `GenerateProtocolApiTask`: invocation of 'Task\.project' at execution time is unsupported\.
 # > Tasks configureCMakeRelWithDebInfo or configureCMakeDebug
-C/C\+\+: (debug|release)\|(x86_64|x86|arm64\-v8a|armeabi\-v7a) :Building ver\.\: [0-9]+\.[0-9]+\.[0-9]+
-C/C\+\+: (debug|release)\|(x86_64|x86|arm64\-v8a|armeabi\-v7a) :Packaging for\: (amd\-[0-9]+|armhf\-[0-9]+|x86\-[0-9]+)
-C/C\+\+: (debug|release)\|(x86_64|x86|arm64\-v8a|armeabi\-v7a) :Compiling for ARM
+C/C\+\+: Building ver\.\: [0-9]+\.[0-9]+\.[0-9]+
+C/C\+\+: Packaging for\: (amd\-[0-9]+|armhf\-[0-9]+|x86\-[0-9]+)
+C/C\+\+: Compiling for ARM
 # > Task :glance:glance:reportLibraryMetrics
 Info: Stripped invalid locals information from [0-9]+ method\.
 Info: Methods with invalid locals information:
diff --git a/development/update_studio.sh b/development/update_studio.sh
index 1b9bdd9..fda43d5 100755
--- a/development/update_studio.sh
+++ b/development/update_studio.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 # Get versions
-AGP_VERSION=${1:-7.2.0-alpha07}
-STUDIO_VERSION_STRING=${2:-"Android Studio Chipmunk (2021.2.1) Canary 7"}
+AGP_VERSION=${1:-7.3.0-alpha01}
+STUDIO_VERSION_STRING=${2:-"Android Studio Dolphin (2021.3.1) Canary 1"}
 STUDIO_IFRAME_LINK=`curl "https://developer.android.com/studio/archive.html" | grep iframe | sed "s/.*src=\"\([a-zA-Z0-9\/\._]*\)\".*/https:\/\/android-dot-devsite-v2-prod.appspot.com\1/g"`
 STUDIO_LINK=`curl -s $STUDIO_IFRAME_LINK | grep -C30 "$STUDIO_VERSION_STRING" | grep Linux | tail -n 1 | sed 's/.*a href="\(.*\).*"/\1/g'`
 STUDIO_VERSION=`echo $STUDIO_LINK | sed "s/.*ide-zips\/\(.*\)\/android-studio-.*/\1/g"`
@@ -28,14 +28,13 @@
 
 # Pull all UTP artifacts for ADT version
 ADT_VERSION=${3:-$LINT_VERSION}
-curl -sL "https://dl.google.com/android/maven2/com/android/tools/utp/group-index.xml" \
-  | tail -n +3 \
-  | head -n -1 \
-  | while read line
+while read line
     do
     ARTIFACT=`echo $line | sed 's/<\([[:lower:]-]\+\).*/\1/g'`
     ARTIFACTS_TO_DOWNLOAD+="com.android.tools.utp:$ARTIFACT:$ADT_VERSION,"
-  done
+  done < <(curl -sL "https://dl.google.com/android/maven2/com/android/tools/utp/group-index.xml" \
+             | tail -n +3 \
+             | head -n -1)
 
 ATP_VERSION=${4:-0.0.8-alpha07}
 ARTIFACTS_TO_DOWNLOAD+="com.google.testing.platform:android-test-plugin:$ATP_VERSION,"
@@ -44,5 +43,4 @@
 ARTIFACTS_TO_DOWNLOAD+="com.google.testing.platform:core:$ATP_VERSION"
 
 # Download all the artifacts
-echo $ARTIFACTS_TO_DOWNLOAD
-./development/importMaven/import_maven_artifacts.py -n $ARTIFACTS_TO_DOWNLOAD
\ No newline at end of file
+./development/importMaven/import_maven_artifacts.py -n "$ARTIFACTS_TO_DOWNLOAD"
\ No newline at end of file
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 9a77c6b..a9097ba 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -260,6 +260,7 @@
     docs("androidx.wear.compose:compose-navigation:1.0.0-alpha15")
     samples("androidx.wear.compose:compose-navigation-samples:1.0.0-alpha15")
     docs("androidx.wear.tiles:tiles:1.1.0-alpha01")
+    docs("androidx.wear.tiles:tiles-material:1.1.0-alpha01")
     docs("androidx.wear.tiles:tiles-proto:1.1.0-alpha01")
     docs("androidx.wear.tiles:tiles-renderer:1.1.0-alpha01")
     docs("androidx.wear.tiles:tiles-testing:1.1.0-alpha01")
diff --git a/draganddrop/draganddrop/src/androidTest/AndroidManifest.xml b/draganddrop/draganddrop/src/androidTest/AndroidManifest.xml
index 8ffc93e..ba8b531 100644
--- a/draganddrop/draganddrop/src/androidTest/AndroidManifest.xml
+++ b/draganddrop/draganddrop/src/androidTest/AndroidManifest.xml
@@ -13,7 +13,7 @@
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    package="androidx.draganddrop">
+    package="androidx.draganddrop.test">
 
     <application
         android:allowBackup="true"
@@ -21,7 +21,7 @@
         android:supportsRtl="true"
         android:theme="@style/Theme.AppCompat.Light.NoActionBar"
         tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon">
-        <activity android:name=".DragAndDropActivity" android:exported="true">
+        <activity android:name="androidx.draganddrop.DragAndDropActivity" android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.DEFAULT"/>
diff --git a/emoji/emoji/src/androidTest/AndroidManifest.xml b/emoji/emoji/src/androidTest/AndroidManifest.xml
index beba93e..1c1136d 100644
--- a/emoji/emoji/src/androidTest/AndroidManifest.xml
+++ b/emoji/emoji/src/androidTest/AndroidManifest.xml
@@ -14,10 +14,10 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="androidx.emoji.text">
+          package="androidx.emoji.test">
 
     <application>
-        <activity android:name=".TestActivity"/>
+        <activity android:name="androidx.emoji.text.TestActivity"/>
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/emoji2/emoji2-bundled/src/androidTest/AndroidManifest.xml b/emoji2/emoji2-bundled/src/androidTest/AndroidManifest.xml
index 29ed14a..8873b81 100644
--- a/emoji2/emoji2-bundled/src/androidTest/AndroidManifest.xml
+++ b/emoji2/emoji2-bundled/src/androidTest/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    package="androidx.emoji2.bundled">
+    package="androidx.emoji2.bundled.test">
 
     <application>
-        <activity android:name=".TestActivity"/>
+        <activity android:name="androidx.emoji2.bundled.TestActivity"/>
         <provider
             android:name="androidx.startup.InitializationProvider"
             android:authorities="${applicationId}.androidx-startup"
diff --git a/fragment/fragment-ktx/build.gradle b/fragment/fragment-ktx/build.gradle
index de40fab..3cd75d6 100644
--- a/fragment/fragment-ktx/build.gradle
+++ b/fragment/fragment-ktx/build.gradle
@@ -39,7 +39,7 @@
         because 'Mirror fragment dependency graph for -ktx artifacts'
     }
     api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-ktx"))
-    api(projectOrArtifact(":savedstate:savedstate-ktx")) {
+    api("androidx.savedstate:savedstate-ktx:1.2.0-alpha01") {
         because 'Mirror fragment dependency graph for -ktx artifacts'
     }
     api(libs.kotlinStdlib)
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index 60abcaf..4282d60 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -33,7 +33,7 @@
     api(projectOrArtifact(":lifecycle:lifecycle-livedata-core"))
     api(projectOrArtifact(":lifecycle:lifecycle-viewmodel"))
     api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-savedstate"))
-    api(projectOrArtifact(":savedstate:savedstate"))
+    api("androidx.savedstate:savedstate:1.2.0-alpha01")
     api("androidx.annotation:annotation-experimental:1.0.0")
     api(libs.kotlinStdlib)
 
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
index 3fe29ea..1477da4 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
@@ -34,6 +34,7 @@
 import android.view.Window;
 import android.view.WindowManager;
 
+import androidx.activity.ComponentDialog;
 import androidx.annotation.IntDef;
 import androidx.annotation.LayoutRes;
 import androidx.annotation.MainThread;
@@ -615,7 +616,7 @@
         if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
             Log.d(TAG, "onCreateDialog called for DialogFragment " + this);
         }
-        return new Dialog(requireContext(), getTheme());
+        return new ComponentDialog(requireContext(), getTheme());
     }
 
     @Override
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 9a2124da..f4ae799 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,13 +2,13 @@
 # -----------------------------------------------------------------------------
 # All of the following should be updated in sync.
 # -----------------------------------------------------------------------------
-androidGradlePlugin = "7.2.0-alpha07"
+androidGradlePlugin = "7.3.0-alpha01"
 # NOTE: When updating the lint version we also need to update the `api` version
 # supported by `IssueRegistry`'s.' For e.g. r.android.com/1331903
-androidLint = "30.2.0-alpha07"
+androidLint = "30.3.0-alpha01"
 # Once you have a chosen version of AGP to upgrade to, go to
 # https://developer.android.com/studio/archive and find the matching version of Studio.
-androidStudio = "2021.2.1.7"
+androidStudio = "2021.3.1.1"
 # -----------------------------------------------------------------------------
 
 androidGradlePluginMin = "7.0.4"
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/ClassPresenterSelector.java b/leanback/leanback/src/main/java/androidx/leanback/widget/ClassPresenterSelector.java
index f202a1f..ab6df8e 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/ClassPresenterSelector.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/ClassPresenterSelector.java
@@ -51,9 +51,10 @@
             PresenterSelector presenterSelector) {
         mClassMap.put(cls, presenterSelector);
         Presenter[] innerPresenters = presenterSelector.getPresenters();
-        for (int i = 0; i < innerPresenters.length; i++)
-        if (!mPresenters.contains(innerPresenters[i])) {
-            mPresenters.add(innerPresenters[i]);
+        for (int i = 0; i < innerPresenters.length; i++) {
+            if (!mPresenters.contains(innerPresenters[i])) {
+                mPresenters.add(innerPresenters[i]);
+            }
         }
         return this;
     }
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/SearchBar.java b/leanback/leanback/src/main/java/androidx/leanback/widget/SearchBar.java
index c875a1b..90a0442 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/SearchBar.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/SearchBar.java
@@ -276,15 +276,15 @@
                     }, 500);
                 } else if (EditorInfo.IME_ACTION_GO == action) {
                     if (DEBUG) Log.v(TAG, "Voice Clicked");
-                        hideNativeKeyboard();
-                        mHandler.postDelayed(new Runnable() {
-                            @Override
-                            public void run() {
-                                if (DEBUG) Log.v(TAG, "Delayed action handling (voice_mode)");
-                                mAutoStartRecognition = true;
-                                mSpeechOrbView.requestFocus();
-                            }
-                        }, 500);
+                    hideNativeKeyboard();
+                    mHandler.postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (DEBUG) Log.v(TAG, "Delayed action handling (voice_mode)");
+                            mAutoStartRecognition = true;
+                            mSpeechOrbView.requestFocus();
+                        }
+                    }, 500);
                 } else {
                     handled = false;
                 }
diff --git a/lifecycle/integration-tests/kotlintestapp/src/test-common/java/androidx.lifecycle/LifecycleCoroutineScopeTestBase.kt b/lifecycle/integration-tests/kotlintestapp/src/test-common/java/androidx.lifecycle/LifecycleCoroutineScopeTestBase.kt
index 02ee0a0..d7b8a69 100644
--- a/lifecycle/integration-tests/kotlintestapp/src/test-common/java/androidx.lifecycle/LifecycleCoroutineScopeTestBase.kt
+++ b/lifecycle/integration-tests/kotlintestapp/src/test-common/java/androidx.lifecycle/LifecycleCoroutineScopeTestBase.kt
@@ -186,10 +186,6 @@
                 throw IllegalArgumentException("why not ?")
             }
             val result = kotlin.runCatching {
-                @Suppress(
-                    "IMPLICIT_NOTHING_AS_TYPE_PARAMETER",
-                    "IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION"
-                )
                 action.await()
             }
             assertThat(result.exceptionOrNull())
diff --git a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/Transformations.kt b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/Transformations.kt
index 656d4b1..3e22243 100644
--- a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/Transformations.kt
+++ b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/Transformations.kt
@@ -79,7 +79,7 @@
  */
 @CheckResult
 public inline fun <X, Y> LiveData<X>.switchMap(
-    crossinline transform: (X) -> LiveData<Y>
+    crossinline transform: (X) -> LiveData<Y>?
 ): LiveData<Y> = Transformations.switchMap(this) { transform(it) }
 
 /**
diff --git a/lifecycle/lifecycle-process/build.gradle b/lifecycle/lifecycle-process/build.gradle
index 3369989..b09c8bc 100644
--- a/lifecycle/lifecycle-process/build.gradle
+++ b/lifecycle/lifecycle-process/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api(project(":lifecycle:lifecycle-runtime"))
-    api("androidx.startup:startup-runtime:1.0.0")
+    api("androidx.startup:startup-runtime:1.1.1")
     api("androidx.annotation:annotation:1.2.0")
 
     testImplementation(libs.junit)
diff --git a/lifecycle/lifecycle-runtime-ktx/src/androidTest/java/androidx/lifecycle/PausingDispatcherTest.kt b/lifecycle/lifecycle-runtime-ktx/src/androidTest/java/androidx/lifecycle/PausingDispatcherTest.kt
index c9a1586..dc8382f 100644
--- a/lifecycle/lifecycle-runtime-ktx/src/androidTest/java/androidx/lifecycle/PausingDispatcherTest.kt
+++ b/lifecycle/lifecycle-runtime-ktx/src/androidTest/java/androidx/lifecycle/PausingDispatcherTest.kt
@@ -270,7 +270,6 @@
     fun throwException_thenRunAnother() {
         runBlocking(testingScope.coroutineContext) {
             try {
-                @Suppress("IMPLICIT_NOTHING_AS_TYPE_PARAMETER")
                 owner.whenResumed {
                     assertThread()
                     expectations.expect(1)
@@ -295,7 +294,6 @@
                     owner.whenResumed {
                         try {
                             expectations.expect(1)
-                            @Suppress("IMPLICIT_NOTHING_AS_TYPE_PARAMETER")
                             withContext(testingScope.coroutineContext) {
                                 throw IllegalStateException("i fail")
                             }
@@ -483,7 +481,6 @@
     @Test
     fun innerJobCancelsParent() {
         try {
-            @Suppress("IMPLICIT_NOTHING_AS_TYPE_PARAMETER")
             runBlocking(testingScope.coroutineContext) {
                 owner.whenResumed {
                     throw IllegalStateException("i fail")
@@ -523,7 +520,6 @@
                             assertThread()
                             expectations.expect(2)
                             try {
-                                @Suppress("IMPLICIT_NOTHING_AS_TYPE_PARAMETER")
                                 withContext(testingScope.coroutineContext) {
                                     throw IllegalStateException("i fail")
                                 }
diff --git a/lifecycle/lifecycle-viewmodel-ktx/src/androidTest/java/androidx/lifecycle/ViewModelTest.kt b/lifecycle/lifecycle-viewmodel-ktx/src/androidTest/java/androidx/lifecycle/ViewModelTest.kt
index 9b51a88..b9990ac 100644
--- a/lifecycle/lifecycle-viewmodel-ktx/src/androidTest/java/androidx/lifecycle/ViewModelTest.kt
+++ b/lifecycle/lifecycle-viewmodel-ktx/src/androidTest/java/androidx/lifecycle/ViewModelTest.kt
@@ -65,10 +65,6 @@
 
         runBlocking {
             try {
-                @Suppress(
-                    "IMPLICIT_NOTHING_AS_TYPE_PARAMETER",
-                    "IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION"
-                )
                 failingDeferred.await()
             } catch (e: Error) {
             }
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateHandleTest.kt b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateHandleTest.kt
index 8e3a3fb..6059aec 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateHandleTest.kt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateHandleTest.kt
@@ -140,6 +140,15 @@
 
     @Test
     @UiThreadTest
+    fun newliveData_withInitialGet() {
+        val handle = SavedStateHandle()
+        val ld: LiveData<String?> = handle.getLiveData("aa", "xx")
+        ld.assertValue("xx")
+        assertThat(handle.get<String?>("aa")).isEqualTo("xx")
+    }
+
+    @Test
+    @UiThreadTest
     fun newLiveData_existingValue_withInitial() {
         val handle = SavedStateHandle()
         handle["aa"] = "existing"
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandle.kt b/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandle.kt
index 7824d94..61add75 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandle.kt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandle.kt
@@ -159,6 +159,7 @@
             @Suppress("UNCHECKED_CAST")
             SavingStateLiveData(this, key, regular[key] as T)
         } else if (hasInitialValue) {
+            regular[key] = initialValue
             SavingStateLiveData(this, key, initialValue)
         } else {
             SavingStateLiveData(this, key)
diff --git a/lifecycle/lifecycle-viewmodel/api/api_lint.ignore b/lifecycle/lifecycle-viewmodel/api/api_lint.ignore
index 2341b8e..190c333 100644
--- a/lifecycle/lifecycle-viewmodel/api/api_lint.ignore
+++ b/lifecycle/lifecycle-viewmodel/api/api_lint.ignore
@@ -1,3 +1,15 @@
 // Baseline format: 1.0
+MissingGetterMatchingBuilder: androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder#addInitializer(kotlin.reflect.KClass<T>, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T>):
+    androidx.lifecycle.viewmodel.InitializerViewModelFactory does not declare a `getInitializers()` method matching method androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder.addInitializer(kotlin.reflect.KClass<T>,kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T>)
+
+
 MissingJvmstatic: androidx.lifecycle.ViewModelProvider.NewInstanceFactory#instance:
     Companion object constants like instance should be using @JvmField, not @JvmStatic; see https://developer.android.com/kotlin/interop#companion_constants
+
+
+SetterReturnsThis: androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder#addInitializer(kotlin.reflect.KClass<T>, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T>):
+    Methods must return the builder object (return type androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder instead of void): method androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder.addInitializer(kotlin.reflect.KClass<T>,kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T>)
+
+
+TopLevelBuilder: androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder:
+    Builder should be defined as inner class: androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder
diff --git a/lifecycle/lifecycle-viewmodel/api/current.txt b/lifecycle/lifecycle-viewmodel/api/current.txt
index 73f1682..5b11ead 100644
--- a/lifecycle/lifecycle-viewmodel/api/current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/current.txt
@@ -48,6 +48,12 @@
   public static interface ViewModelProvider.Factory {
     method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass);
     method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass, androidx.lifecycle.viewmodel.CreationExtras extras);
+    method public default static androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
+    field public static final androidx.lifecycle.ViewModelProvider.Factory.Companion Companion;
+  }
+
+  public static final class ViewModelProvider.Factory.Companion {
+    method public androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
   }
 
   public static class ViewModelProvider.NewInstanceFactory implements androidx.lifecycle.ViewModelProvider.Factory {
@@ -97,11 +103,29 @@
   public static interface CreationExtras.Key<T> {
   }
 
+  @androidx.lifecycle.viewmodel.ViewModelFactoryDsl public final class InitializerViewModelFactoryBuilder {
+    ctor public InitializerViewModelFactoryBuilder();
+    method public <T extends androidx.lifecycle.ViewModel> void addInitializer(kotlin.reflect.KClass<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+    method public androidx.lifecycle.ViewModelProvider.Factory build();
+  }
+
+  public final class InitializerViewModelFactoryKt {
+    method public static inline <reified VM extends androidx.lifecycle.ViewModel> void initializer(androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
+    method public static inline androidx.lifecycle.ViewModelProvider.Factory viewModelFactory(kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder,kotlin.Unit> builder);
+  }
+
   public final class MutableCreationExtras extends androidx.lifecycle.viewmodel.CreationExtras {
     ctor public MutableCreationExtras(optional androidx.lifecycle.viewmodel.CreationExtras initialExtras);
     method public <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
     method public operator <T> void set(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key, T? t);
   }
 
+  @kotlin.DslMarker public @interface ViewModelFactoryDsl {
+  }
+
+  public final class ViewModelInitializer<T extends androidx.lifecycle.ViewModel> {
+    ctor public ViewModelInitializer(Class<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+  }
+
 }
 
diff --git a/lifecycle/lifecycle-viewmodel/api/public_plus_experimental_current.txt b/lifecycle/lifecycle-viewmodel/api/public_plus_experimental_current.txt
index 73f1682..5b11ead 100644
--- a/lifecycle/lifecycle-viewmodel/api/public_plus_experimental_current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/public_plus_experimental_current.txt
@@ -48,6 +48,12 @@
   public static interface ViewModelProvider.Factory {
     method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass);
     method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass, androidx.lifecycle.viewmodel.CreationExtras extras);
+    method public default static androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
+    field public static final androidx.lifecycle.ViewModelProvider.Factory.Companion Companion;
+  }
+
+  public static final class ViewModelProvider.Factory.Companion {
+    method public androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
   }
 
   public static class ViewModelProvider.NewInstanceFactory implements androidx.lifecycle.ViewModelProvider.Factory {
@@ -97,11 +103,29 @@
   public static interface CreationExtras.Key<T> {
   }
 
+  @androidx.lifecycle.viewmodel.ViewModelFactoryDsl public final class InitializerViewModelFactoryBuilder {
+    ctor public InitializerViewModelFactoryBuilder();
+    method public <T extends androidx.lifecycle.ViewModel> void addInitializer(kotlin.reflect.KClass<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+    method public androidx.lifecycle.ViewModelProvider.Factory build();
+  }
+
+  public final class InitializerViewModelFactoryKt {
+    method public static inline <reified VM extends androidx.lifecycle.ViewModel> void initializer(androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
+    method public static inline androidx.lifecycle.ViewModelProvider.Factory viewModelFactory(kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder,kotlin.Unit> builder);
+  }
+
   public final class MutableCreationExtras extends androidx.lifecycle.viewmodel.CreationExtras {
     ctor public MutableCreationExtras(optional androidx.lifecycle.viewmodel.CreationExtras initialExtras);
     method public <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
     method public operator <T> void set(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key, T? t);
   }
 
+  @kotlin.DslMarker public @interface ViewModelFactoryDsl {
+  }
+
+  public final class ViewModelInitializer<T extends androidx.lifecycle.ViewModel> {
+    ctor public ViewModelInitializer(Class<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+  }
+
 }
 
diff --git a/lifecycle/lifecycle-viewmodel/api/restricted_current.txt b/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
index 73f1682..5b11ead 100644
--- a/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
@@ -48,6 +48,12 @@
   public static interface ViewModelProvider.Factory {
     method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass);
     method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass, androidx.lifecycle.viewmodel.CreationExtras extras);
+    method public default static androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
+    field public static final androidx.lifecycle.ViewModelProvider.Factory.Companion Companion;
+  }
+
+  public static final class ViewModelProvider.Factory.Companion {
+    method public androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
   }
 
   public static class ViewModelProvider.NewInstanceFactory implements androidx.lifecycle.ViewModelProvider.Factory {
@@ -97,11 +103,29 @@
   public static interface CreationExtras.Key<T> {
   }
 
+  @androidx.lifecycle.viewmodel.ViewModelFactoryDsl public final class InitializerViewModelFactoryBuilder {
+    ctor public InitializerViewModelFactoryBuilder();
+    method public <T extends androidx.lifecycle.ViewModel> void addInitializer(kotlin.reflect.KClass<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+    method public androidx.lifecycle.ViewModelProvider.Factory build();
+  }
+
+  public final class InitializerViewModelFactoryKt {
+    method public static inline <reified VM extends androidx.lifecycle.ViewModel> void initializer(androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
+    method public static inline androidx.lifecycle.ViewModelProvider.Factory viewModelFactory(kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder,kotlin.Unit> builder);
+  }
+
   public final class MutableCreationExtras extends androidx.lifecycle.viewmodel.CreationExtras {
     ctor public MutableCreationExtras(optional androidx.lifecycle.viewmodel.CreationExtras initialExtras);
     method public <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
     method public operator <T> void set(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key, T? t);
   }
 
+  @kotlin.DslMarker public @interface ViewModelFactoryDsl {
+  }
+
+  public final class ViewModelInitializer<T extends androidx.lifecycle.ViewModel> {
+    ctor public ViewModelInitializer(Class<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+  }
+
 }
 
diff --git a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.kt b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.kt
index bcd2750..d4d3d43 100644
--- a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.kt
+++ b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.kt
@@ -23,7 +23,9 @@
 import androidx.lifecycle.viewmodel.CreationExtras.Key
 import androidx.lifecycle.ViewModelProvider.NewInstanceFactory.Companion.VIEW_MODEL_KEY
 import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.InitializerViewModelFactory
 import androidx.lifecycle.viewmodel.MutableCreationExtras
+import androidx.lifecycle.viewmodel.ViewModelInitializer
 import java.lang.IllegalArgumentException
 import java.lang.RuntimeException
 import java.lang.reflect.InvocationTargetException
@@ -77,6 +79,18 @@
          */
         public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =
             create(modelClass)
+
+        companion object {
+            /**
+             * Creates an [InitializerViewModelFactory] using the given initializers.
+             *
+             * @param initializers the class initializer pairs used for the factory to create
+             * simple view models
+             */
+            @JvmStatic
+            fun from(vararg initializers: ViewModelInitializer<*>): Factory =
+                InitializerViewModelFactory(*initializers)
+        }
     }
 
     /**
@@ -350,4 +364,4 @@
  * @see ViewModelProvider.get(Class)
  */
 @MainThread
-public inline fun <reified VM : ViewModel> ViewModelProvider.get(): VM = get(VM::class.java)
\ No newline at end of file
+public inline fun <reified VM : ViewModel> ViewModelProvider.get(): VM = get(VM::class.java)
diff --git a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/viewmodel/InitializerViewModelFactory.kt b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/viewmodel/InitializerViewModelFactory.kt
new file mode 100644
index 0000000..2c005d8
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/viewmodel/InitializerViewModelFactory.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2022 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.lifecycle.viewmodel
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import kotlin.reflect.KClass
+
+@DslMarker
+public annotation class ViewModelFactoryDsl
+
+/**
+ * Creates an [InitializerViewModelFactory] with the initializers provided in the builder.
+ */
+public inline fun viewModelFactory(
+    builder: InitializerViewModelFactoryBuilder.() -> Unit
+): ViewModelProvider.Factory = InitializerViewModelFactoryBuilder().apply(builder).build()
+
+/**
+ * DSL for constructing a new [ViewModelProvider.Factory]
+ */
+@ViewModelFactoryDsl
+public class InitializerViewModelFactoryBuilder {
+    private val initializers = mutableListOf<ViewModelInitializer<*>>()
+
+    /**
+     * Add the initializer for the given ViewModel class.
+     *
+     * @param clazz the class the initializer is associated with.
+     * @param initializer lambda used to create an instance of the ViewModel class
+     */
+    fun <T : ViewModel> addInitializer(clazz: KClass<T>, initializer: CreationExtras.() -> T) {
+        initializers.add(ViewModelInitializer(clazz.java, initializer))
+    }
+
+    /**
+     * Build the InitializerViewModelFactory.
+     */
+    fun build(): ViewModelProvider.Factory =
+        InitializerViewModelFactory(*initializers.toTypedArray())
+}
+
+/**
+ * Add an initializer to the [InitializerViewModelFactoryBuilder]
+ */
+inline fun <reified VM : ViewModel> InitializerViewModelFactoryBuilder.initializer(
+    noinline initializer: CreationExtras.() -> VM
+) {
+    addInitializer(VM::class, initializer)
+}
+
+/**
+ * Holds a [ViewModel] class and initializer for that class
+ */
+class ViewModelInitializer<T : ViewModel>(
+    internal val clazz: Class<T>,
+    internal val initializer: CreationExtras.() -> T,
+)
+
+/**
+ * A [ViewModelProvider.Factory] that allows you to add lambda initializers for handling
+ * particular ViewModel classes using [CreationExtras], while using the default behavior for any
+ * other classes.
+ *
+ * ```
+ * val factory = viewModelFactory {
+ *   initializer { TestViewModel(this[key]) }
+ * }
+ * val viewModel: TestViewModel = factory.create(TestViewModel::class.java, extras)
+ * ```
+ */
+internal class InitializerViewModelFactory(
+    private vararg val initializers: ViewModelInitializer<*>
+) : ViewModelProvider.Factory {
+
+    /**
+     * Creates a new instance of the given `Class`.
+     *
+     * This will use the initializer if one has been set for the class, otherwise it throw an
+     * [IllegalArgumentException].
+     *
+     * @param modelClass a `Class` whose instance is requested
+     * @param extras an additional information for this creation request
+     * @return a newly created ViewModel
+     *
+     * @throws IllegalArgumentException if no initializer has been set for the given class.
+     */
+    override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
+        var viewModel: T? = null
+        @Suppress("UNCHECKED_CAST")
+        initializers.forEach {
+            if (it.clazz == modelClass) {
+                viewModel = it.initializer.invoke(extras) as? T
+            }
+        }
+        return viewModel ?: throw IllegalArgumentException(
+            "No initializer set for given class ${modelClass.name}"
+        )
+    }
+}
diff --git a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/viewmodel/ViewModelInitializerTest.kt b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/viewmodel/ViewModelInitializerTest.kt
new file mode 100644
index 0000000..d9d3d1f
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/viewmodel/ViewModelInitializerTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2022 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.lifecycle.viewmodel
+
+import androidx.lifecycle.ViewModel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ViewModelInitializerTest {
+    @Test
+    fun testInitializerFactory() {
+        val key = object : CreationExtras.Key<String> {}
+        val value1 = "test_value1"
+        val extras1 = MutableCreationExtras().apply { set(key, value1) }
+        val value2 = "test_value2"
+        val extras2 = MutableCreationExtras().apply { set(key, value2) }
+        val factory = viewModelFactory {
+            initializer { TestViewModel1(extras1[key]) }
+            initializer { TestViewModel2(extras2[key]) }
+        }
+        val viewModel1: TestViewModel1 = factory.create(TestViewModel1::class.java, extras1)
+        val viewModel2: TestViewModel2 = factory.create(TestViewModel2::class.java, extras2)
+        assertThat(viewModel1.value).isEqualTo(value1)
+        assertThat(viewModel2.value).isEqualTo(value2)
+    }
+
+    @Test
+    fun testInitializerFactoryNoInitializer() {
+        val key = object : CreationExtras.Key<String> {}
+        val value = "test_value"
+        val extras = MutableCreationExtras().apply { set(key, value) }
+        val factory = viewModelFactory { }
+        try {
+            factory.create(TestViewModel1::class.java, extras)
+        } catch (e: IllegalArgumentException) {
+            assertThat(e).hasMessageThat().isEqualTo(
+                "No initializer set for given class ${TestViewModel1::class.java.name}"
+            )
+        }
+    }
+}
+
+private class TestViewModel1(val value: String?) : ViewModel()
+
+private class TestViewModel2(val value: String?) : ViewModel()
diff --git a/media/media/api/current.txt b/media/media/api/current.txt
index 13e609b..89e9bd3 100644
--- a/media/media/api/current.txt
+++ b/media/media/api/current.txt
@@ -714,6 +714,7 @@
     field public static final String BROWSER_ROOT_HINTS_KEY_MEDIA_ART_SIZE_PIXELS = "android.media.extras.MEDIA_ART_SIZE_HINT_PIXELS";
     field public static final String BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT = "androidx.media.MediaBrowserCompat.Extras.KEY_ROOT_CHILDREN_LIMIT";
     field public static final String BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS = "androidx.media.MediaBrowserCompat.Extras.KEY_ROOT_CHILDREN_SUPPORTED_FLAGS";
+    field public static final String BROWSER_SERVICE_EXTRAS_KEY_APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT = "androidx.media.BrowserRoot.Extras.APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT";
     field public static final String BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED = "android.media.browse.SEARCH_SUPPORTED";
     field public static final String DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE = "androidx.media.MediaItem.Extras.COMPLETION_PERCENTAGE";
     field public static final String DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS = "android.media.extra.PLAYBACK_STATUS";
@@ -736,6 +737,7 @@
     field public static final long METADATA_VALUE_ATTRIBUTE_PRESENT = 1L; // 0x1L
     field public static final String PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT = "android.media.extras.ERROR_RESOLUTION_ACTION_INTENT";
     field public static final String PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL = "android.media.extras.ERROR_RESOLUTION_ACTION_LABEL";
+    field public static final String PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_USING_CAR_APP_LIBRARY_INTENT = "androidx.media.PlaybackStateCompat.Extras.ERROR_RESOLUTION_USING_CAR_APP_LIBRARY_INTENT";
     field public static final String PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID = "androidx.media.PlaybackStateCompat.Extras.KEY_MEDIA_ID";
     field public static final String SESSION_EXTRAS_KEY_ACCOUNT_NAME = "androidx.media.MediaSessionCompat.Extras.KEY_ACCOUNT_NAME";
     field public static final String SESSION_EXTRAS_KEY_ACCOUNT_TYPE = "androidx.media.MediaSessionCompat.Extras.KEY_ACCOUNT_TYPE";
diff --git a/media/media/api/public_plus_experimental_current.txt b/media/media/api/public_plus_experimental_current.txt
index 13e609b..89e9bd3 100644
--- a/media/media/api/public_plus_experimental_current.txt
+++ b/media/media/api/public_plus_experimental_current.txt
@@ -714,6 +714,7 @@
     field public static final String BROWSER_ROOT_HINTS_KEY_MEDIA_ART_SIZE_PIXELS = "android.media.extras.MEDIA_ART_SIZE_HINT_PIXELS";
     field public static final String BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT = "androidx.media.MediaBrowserCompat.Extras.KEY_ROOT_CHILDREN_LIMIT";
     field public static final String BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS = "androidx.media.MediaBrowserCompat.Extras.KEY_ROOT_CHILDREN_SUPPORTED_FLAGS";
+    field public static final String BROWSER_SERVICE_EXTRAS_KEY_APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT = "androidx.media.BrowserRoot.Extras.APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT";
     field public static final String BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED = "android.media.browse.SEARCH_SUPPORTED";
     field public static final String DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE = "androidx.media.MediaItem.Extras.COMPLETION_PERCENTAGE";
     field public static final String DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS = "android.media.extra.PLAYBACK_STATUS";
@@ -736,6 +737,7 @@
     field public static final long METADATA_VALUE_ATTRIBUTE_PRESENT = 1L; // 0x1L
     field public static final String PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT = "android.media.extras.ERROR_RESOLUTION_ACTION_INTENT";
     field public static final String PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL = "android.media.extras.ERROR_RESOLUTION_ACTION_LABEL";
+    field public static final String PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_USING_CAR_APP_LIBRARY_INTENT = "androidx.media.PlaybackStateCompat.Extras.ERROR_RESOLUTION_USING_CAR_APP_LIBRARY_INTENT";
     field public static final String PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID = "androidx.media.PlaybackStateCompat.Extras.KEY_MEDIA_ID";
     field public static final String SESSION_EXTRAS_KEY_ACCOUNT_NAME = "androidx.media.MediaSessionCompat.Extras.KEY_ACCOUNT_NAME";
     field public static final String SESSION_EXTRAS_KEY_ACCOUNT_TYPE = "androidx.media.MediaSessionCompat.Extras.KEY_ACCOUNT_TYPE";
diff --git a/media/media/api/restricted_current.txt b/media/media/api/restricted_current.txt
index 8c6f90e..ef3e28f 100644
--- a/media/media/api/restricted_current.txt
+++ b/media/media/api/restricted_current.txt
@@ -752,6 +752,7 @@
     field public static final String BROWSER_ROOT_HINTS_KEY_MEDIA_ART_SIZE_PIXELS = "android.media.extras.MEDIA_ART_SIZE_HINT_PIXELS";
     field public static final String BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT = "androidx.media.MediaBrowserCompat.Extras.KEY_ROOT_CHILDREN_LIMIT";
     field public static final String BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS = "androidx.media.MediaBrowserCompat.Extras.KEY_ROOT_CHILDREN_SUPPORTED_FLAGS";
+    field public static final String BROWSER_SERVICE_EXTRAS_KEY_APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT = "androidx.media.BrowserRoot.Extras.APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT";
     field public static final String BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED = "android.media.browse.SEARCH_SUPPORTED";
     field public static final String DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE = "androidx.media.MediaItem.Extras.COMPLETION_PERCENTAGE";
     field public static final String DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS = "android.media.extra.PLAYBACK_STATUS";
@@ -774,6 +775,7 @@
     field public static final long METADATA_VALUE_ATTRIBUTE_PRESENT = 1L; // 0x1L
     field public static final String PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT = "android.media.extras.ERROR_RESOLUTION_ACTION_INTENT";
     field public static final String PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL = "android.media.extras.ERROR_RESOLUTION_ACTION_LABEL";
+    field public static final String PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_USING_CAR_APP_LIBRARY_INTENT = "androidx.media.PlaybackStateCompat.Extras.ERROR_RESOLUTION_USING_CAR_APP_LIBRARY_INTENT";
     field public static final String PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID = "androidx.media.PlaybackStateCompat.Extras.KEY_MEDIA_ID";
     field public static final String SESSION_EXTRAS_KEY_ACCOUNT_NAME = "androidx.media.MediaSessionCompat.Extras.KEY_ACCOUNT_NAME";
     field public static final String SESSION_EXTRAS_KEY_ACCOUNT_TYPE = "androidx.media.MediaSessionCompat.Extras.KEY_ACCOUNT_TYPE";
diff --git a/media/media/src/main/java/androidx/media/utils/MediaConstants.java b/media/media/src/main/java/androidx/media/utils/MediaConstants.java
index f353b2a..a2b21c2 100644
--- a/media/media/src/main/java/androidx/media/utils/MediaConstants.java
+++ b/media/media/src/main/java/androidx/media/utils/MediaConstants.java
@@ -255,6 +255,25 @@
             "android.media.extras.MEDIA_ART_SIZE_HINT_PIXELS";
 
     /**
+     * Bundle key sent through {@link MediaBrowserCompat#getExtras()} to the {@link
+     * MediaBrowserCompat} to indicate that the {@link MediaBrowserServiceCompat} supports showing
+     * a settings page.  The intent should have the component name set to a Car App Library service
+     * which exists in the same package as the media browser service.
+     *
+     * <p>TYPE: {@link Intent}. Should be inserted into the Bundle {@link
+     * Bundle#putParcelable(String, Parcelable) as a Parcelable}.
+     *
+     * @see MediaBrowserCompat#getExtras()
+     * @see MediaBrowserServiceCompat.BrowserRoot#BrowserRoot(String, Bundle)
+     * @see #PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT
+     */
+    @SuppressLint("IntentName")
+    public static final String
+            BROWSER_SERVICE_EXTRAS_KEY_APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT =
+            "androidx.media.BrowserRoot.Extras"
+                    + ".APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT";
+
+    /**
      * Bundle key sent through {@link MediaBrowserCompat#getExtras()} to the hosting {@link
      * MediaBrowserCompat} to indicate that the {@link MediaBrowserServiceCompat} supports the
      * method {@link MediaBrowserServiceCompat#onSearch(String, Bundle,
@@ -510,7 +529,9 @@
      * MediaControllerCompat} which maps to a pending intent. When launched, the intent should allow
      * users to resolve the current playback state error. {@link
      * #PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL A label} should be included in the
-     * same Bundle.
+     * same Bundle. The key {@link
+     * #BROWSER_SERVICE_EXTRAS_KEY_APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT} should be
+     * used instead if the intent points to a Car App Library service.
      *
      * <p>TYPE: PendingIntent. Should be inserted into the Bundle {@link
      * Bundle#putParcelable(String, Parcelable) as a Parcelable}.
@@ -518,12 +539,32 @@
      * @see PlaybackStateCompat#getExtras()
      * @see PlaybackStateCompat.Builder#setExtras(Bundle)
      * @see #PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL
+     * @see #BROWSER_SERVICE_EXTRAS_KEY_APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT
      */
     @SuppressLint("IntentName")
     public static final String PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT =
             "android.media.extras.ERROR_RESOLUTION_ACTION_INTENT";
 
     /**
+     * Bundle key passed through {@link PlaybackStateCompat#getExtras()} to the {@link
+     * MediaControllerCompat} which maps to an intent. When launched, the intent should allow
+     * users to resolve the current playback state error. The intent should have the component name
+     * set to a Car App Library service which exists in the same package as the media browser
+     * service.
+     *
+     * <p>TYPE: {@link Intent}. Should be inserted into the Bundle {@link
+     * Bundle#putParcelable(String, Parcelable) as a Parcelable}.
+     *
+     * @see PlaybackStateCompat#getExtras()
+     * @see PlaybackStateCompat.Builder#setExtras(Bundle)
+     */
+    @SuppressLint("IntentName")
+    public static final String
+            PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_USING_CAR_APP_LIBRARY_INTENT =
+            "androidx.media.PlaybackStateCompat.Extras"
+                   + ".ERROR_RESOLUTION_USING_CAR_APP_LIBRARY_INTENT";
+
+    /**
      * Bundle key passed through the {@code extras} of
      * {@link MediaControllerCompat.TransportControls#prepareFromMediaId(String, Bundle)},
      * {@link MediaControllerCompat.TransportControls#prepareFromSearch(String, Bundle)},
diff --git a/navigation/navigation-common/build.gradle b/navigation/navigation-common/build.gradle
index 429b6a2..66ca350 100644
--- a/navigation/navigation-common/build.gradle
+++ b/navigation/navigation-common/build.gradle
@@ -33,7 +33,7 @@
     api("androidx.annotation:annotation:1.1.0")
     api(projectOrArtifact(":lifecycle:lifecycle-runtime-ktx"))
     api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-ktx"))
-    api(projectOrArtifact(":savedstate:savedstate-ktx"))
+    api("androidx.savedstate:savedstate-ktx:1.2.0-alpha01")
     api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-savedstate"))
     implementation("androidx.core:core-ktx:1.1.0")
     implementation("androidx.collection:collection-ktx:1.1.0")
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt
index 183f591..7abf529 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt
@@ -114,9 +114,7 @@
     private var lifecycle = LifecycleRegistry(this)
     private val savedStateRegistryController = SavedStateRegistryController.create(this)
     private var savedStateRegistryAttached = false
-    private val defaultFactory by lazy {
-        SavedStateViewModelFactory((context?.applicationContext as? Application), this, arguments)
-    }
+    private val defaultFactory by lazy { SavedStateViewModelFactory() }
 
     /**
      * The [SavedStateHandle] for this entry.
@@ -132,7 +130,7 @@
                 "NavBackStackEntry is destroyed."
         }
         ViewModelProvider(
-            this, NavResultSavedStateFactory(this, null)
+            this, NavResultSavedStateFactory(this)
         ).get(SavedStateViewModel::class.java).handle
     }
 
@@ -268,9 +266,8 @@
      * Used to create the {SavedStateViewModel}
      */
     private class NavResultSavedStateFactory(
-        owner: SavedStateRegistryOwner,
-        defaultArgs: Bundle?
-    ) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
+        owner: SavedStateRegistryOwner
+    ) : AbstractSavedStateViewModelFactory(owner, null) {
         @Suppress("UNCHECKED_CAST")
         override fun <T : ViewModel> create(
             key: String,
diff --git a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
index 15d6fe5..d8f0c9d 100644
--- a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
+++ b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
@@ -271,27 +271,30 @@
     @Test
     fun testViewModelSavedAfterConfigChange() {
         lateinit var navController: NavHostController
+        var lifecycleOwner = TestLifecycleOwner(Lifecycle.State.RESUMED)
         lateinit var state: MutableState<Int>
         lateinit var viewModel: TestViewModel
         var savedState: Bundle? = null
         composeTestRule.setContent {
             val context = LocalContext.current
             state = remember { mutableStateOf(0) }
-            navController = if (savedState == null) {
-                rememberNavController()
-            } else {
-                NavHostController(context).apply {
-                    restoreState(savedState)
-                    setViewModelStore(LocalViewModelStoreOwner.current!!.viewModelStore)
-                    navigatorProvider += ComposeNavigator()
-                    navigatorProvider += DialogNavigator()
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                navController = if (savedState == null) {
+                    rememberNavController()
+                } else {
+                    NavHostController(context).apply {
+                        restoreState(savedState)
+                        setViewModelStore(LocalViewModelStoreOwner.current!!.viewModelStore)
+                        navigatorProvider += ComposeNavigator()
+                        navigatorProvider += DialogNavigator()
+                    }
                 }
-            }
-            if (state.value == 0) {
-                NavHost(navController, startDestination = "first") {
-                    composable("first") {
-                        val provider = ViewModelProvider(it)
-                        viewModel = provider.get("key", TestViewModel::class.java)
+                if (state.value == 0) {
+                    NavHost(navController, startDestination = "first") {
+                        composable("first") {
+                            val provider = ViewModelProvider(it)
+                            viewModel = provider.get("key", TestViewModel::class.java)
+                        }
                     }
                 }
             }
@@ -303,11 +306,13 @@
         runOnUiThread {
             // dispose the NavHost
             state.value = 1
+            lifecycleOwner.currentState = Lifecycle.State.DESTROYED
         }
 
         // wait for recompose without NavHost then recompose with the NavHost
         composeTestRule.runOnIdle {
             state.value = 0
+            lifecycleOwner = TestLifecycleOwner(Lifecycle.State.RESUMED)
         }
 
         composeTestRule.runOnIdle {
@@ -320,28 +325,31 @@
     @Test
     fun testSaveableStateClearedAfterConfigChange() {
         lateinit var navController: NavHostController
+        var lifecycleOwner = TestLifecycleOwner(Lifecycle.State.RESUMED)
         lateinit var state: MutableState<Int>
         var viewModel: BackStackEntryIdViewModel? = null
         var savedState: Bundle? = null
         composeTestRule.setContent {
             val context = LocalContext.current
-            state = remember { mutableStateOf(0) }
-            navController = if (savedState == null) {
-                rememberNavController()
-            } else {
-                NavHostController(context).apply {
-                    restoreState(savedState)
-                    setViewModelStore(LocalViewModelStoreOwner.current!!.viewModelStore)
-                    navigatorProvider += ComposeNavigator()
-                    navigatorProvider += DialogNavigator()
-                }
-            }
-            if (state.value == 0) {
-                NavHost(navController, startDestination = "first") {
-                    composable("first") {
-                        viewModel = viewModel()
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                state = remember { mutableStateOf(0) }
+                navController = if (savedState == null) {
+                    rememberNavController()
+                } else {
+                    NavHostController(context).apply {
+                        restoreState(savedState)
+                        setViewModelStore(LocalViewModelStoreOwner.current!!.viewModelStore)
+                        navigatorProvider += ComposeNavigator()
+                        navigatorProvider += DialogNavigator()
                     }
-                    composable("second") { }
+                }
+                if (state.value == 0) {
+                    NavHost(navController, startDestination = "first") {
+                        composable("first") {
+                            viewModel = viewModel()
+                        }
+                        composable("second") { }
+                    }
                 }
             }
         }
@@ -357,11 +365,13 @@
         runOnUiThread {
             // dispose the NavHost
             state.value = 1
+            lifecycleOwner.currentState = Lifecycle.State.DESTROYED
         }
 
         // wait for recompose without NavHost then recompose with the NavHost
         composeTestRule.runOnIdle {
             state.value = 0
+            lifecycleOwner = TestLifecycleOwner(Lifecycle.State.RESUMED)
         }
 
         composeTestRule.runOnIdle {
@@ -802,6 +812,7 @@
     @Test
     fun testNestedNavHostOnBackPressed() {
         val lifecycleOwner = TestLifecycleOwner()
+        var innerLifecycleOwner = TestLifecycleOwner(Lifecycle.State.RESUMED)
         val onBackPressedDispatcher = OnBackPressedDispatcher()
         val dispatcherOwner = object : OnBackPressedDispatcherOwner {
             override fun getLifecycle() = lifecycleOwner.lifecycle
@@ -815,12 +826,14 @@
                 navController = rememberNavController()
                 NavHost(navController, first) {
                     composable(first) {
-                        // Note: you should not ever do this. Use the state of the single
-                        // NavHost to control the visibility of global UI
-                        innerNavController = rememberNavController()
-                        NavHost(innerNavController, "innerFirst") {
-                            composable("innerFirst") {}
-                            composable("innerSecond") {}
+                        CompositionLocalProvider(LocalLifecycleOwner provides innerLifecycleOwner) {
+                            // Note: you should not ever do this. Use the state of the single
+                            // NavHost to control the visibility of global UI
+                            innerNavController = rememberNavController()
+                            NavHost(innerNavController, "innerFirst") {
+                                composable("innerFirst") {}
+                                composable("innerSecond") {}
+                            }
                         }
                     }
                     composable(second) {}
@@ -837,11 +850,13 @@
         // Now navigate to a second destination in the outer NavHost
         composeTestRule.runOnIdle {
             navController.navigate(second)
+            innerLifecycleOwner.currentState = Lifecycle.State.DESTROYED
         }
 
         // Now trigger the back button
         composeTestRule.runOnIdle {
             onBackPressedDispatcher.onBackPressed()
+            innerLifecycleOwner = TestLifecycleOwner(Lifecycle.State.RESUMED)
         }
 
         composeTestRule.waitForIdle()
diff --git a/navigation/navigation-runtime/build.gradle b/navigation/navigation-runtime/build.gradle
index f52b59c..2e16806 100644
--- a/navigation/navigation-runtime/build.gradle
+++ b/navigation/navigation-runtime/build.gradle
@@ -30,6 +30,7 @@
     api(projectOrArtifact(":lifecycle:lifecycle-runtime-ktx"))
     api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-ktx"))
     api("androidx.annotation:annotation-experimental:1.1.0")
+    implementation('androidx.collection:collection:1.0.0')
 
     api(libs.kotlinStdlib)
     androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt
index a6bbced..4235c67 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt
@@ -17,7 +17,9 @@
 package androidx.navigation
 
 import android.app.Application
+import androidx.core.os.bundleOf
 import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.DEFAULT_ARGS_KEY
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.ViewModel
@@ -25,6 +27,7 @@
 import androidx.lifecycle.ViewModelStore
 import androidx.lifecycle.get
 import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.lifecycle.viewmodel.MutableCreationExtras
 import androidx.navigation.test.R
 import androidx.test.annotation.UiThreadTest
 import androidx.test.core.app.ActivityScenario
@@ -121,7 +124,11 @@
     @Test
     fun testGetViewModelStoreOwnerSavedStateViewModel() {
         val hostStore = ViewModelStore()
-        val navController = createNavController()
+        val lifecycleOwner = TestLifecycleOwner(Lifecycle.State.RESUMED)
+        val navController = NavHostController(ApplicationProvider.getApplicationContext()).apply {
+            setLifecycleOwner(lifecycleOwner)
+            navigatorProvider.addNavigator(TestNavigator())
+        }
         navController.setViewModelStore(hostStore)
         val navGraph = navController.navigatorProvider.navigation(
             id = 1,
@@ -139,12 +146,17 @@
         viewModel.savedStateHandle.set("test", "test")
 
         val savedState = navController.saveState()
+
+        // Need to move the old navController state to DESTROYED to detach
+        // SavedStateHandleController
+        lifecycleOwner.lifecycle.currentState = Lifecycle.State.DESTROYED
+
         val restoredNavController = createNavController()
         restoredNavController.setViewModelStore(hostStore)
         restoredNavController.restoreState(savedState)
         restoredNavController.graph = navGraph
 
-        val restoredOwner = navController.getViewModelStoreOwner(navGraph.id)
+        val restoredOwner = restoredNavController.getViewModelStoreOwner(navGraph.id)
         val restoredViewModel = ViewModelProvider(
             restoredOwner
         )[TestSavedStateViewModel::class.java]
@@ -262,7 +274,11 @@
     @Test
     fun testCreateViewModelViaExtras() {
         val hostStore = ViewModelStore()
-        val navController = createNavController()
+        val lifecycleOwner = TestLifecycleOwner(Lifecycle.State.RESUMED)
+        val navController = NavHostController(ApplicationProvider.getApplicationContext()).apply {
+            setLifecycleOwner(lifecycleOwner)
+            navigatorProvider.addNavigator(TestNavigator())
+        }
         navController.setViewModelStore(hostStore)
         val navGraph = navController.navigatorProvider.navigation(
             id = 1,
@@ -274,31 +290,19 @@
 
         val entry = navController.currentBackStackEntry!!
 
-        val creationViewModel = ViewModelProvider(
-            entry.viewModelStore,
-            entry.defaultViewModelProviderFactory,
-            entry.defaultViewModelCreationExtras)["test", TestSavedStateViewModel::class.java]
-
-        val key = "key"
-        val value = "value"
-
-        creationViewModel.savedStateHandle.set(key, value)
-
-        val savedState = navController.saveState()
-        val restoredNavController = createNavController()
-        restoredNavController.setViewModelStore(hostStore)
-        restoredNavController.restoreState(savedState)
-        restoredNavController.graph = navGraph
-
-        val restoredEntry = restoredNavController.currentBackStackEntry!!
+        val extras = MutableCreationExtras(entry.defaultViewModelCreationExtras).apply {
+            this[DEFAULT_ARGS_KEY] = bundleOf("test" to "value")
+        }
 
         val actualValue =
-            ViewModelProvider(restoredEntry)["test", TestSavedStateViewModel::class.java]
-                .savedStateHandle.get<String>(key)
+            ViewModelProvider(
+                entry.viewModelStore,
+                entry.defaultViewModelProviderFactory,
+                extras)["test", TestSavedStateViewModel::class.java].defaultValue
 
-        assertWithMessage("Restored back stack entry should have same ViewModel instance")
+        assertWithMessage("ViewModel from back stack entry should have args from CreationExtras")
             .that(actualValue)
-            .isEqualTo(value)
+            .isEqualTo("value")
     }
 
     @UiThreadTest
@@ -645,4 +649,6 @@
     }
 }
 
-class TestSavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel()
+class TestSavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel() {
+    val defaultValue = savedStateHandle.get<String>("test")
+}
diff --git a/playground-common/playground-plugin/settings.gradle b/playground-common/playground-plugin/settings.gradle
index cb8499c..03d34d9 100644
--- a/playground-common/playground-plugin/settings.gradle
+++ b/playground-common/playground-plugin/settings.gradle
@@ -15,14 +15,21 @@
  */
 rootProject.name = "playground-plugin"
 
-// Build cache configuration is duplicated here, so that when building the `playground-plugin` 
-// included build the remote build cache will be used.
+// Build cache configuration is duplicated here from the GradleEnterpriseConventionsPlugin,
+// so that when building the `playground-plugin` included build the same build cache settings will be used.
 // Without this, Gradle Enterprise erroneously reports a problem with 'buildSrc' build cache configuration.
+def isCI = System.getenv("CI") == "true"
 buildCache {
+    local {
+        // Aggressively clean up stale build cache entries on CI
+        if (isCI) {
+            removeUnusedEntriesAfterDays = 1
+        }
+    }
     remote(HttpBuildCache) {
         url = "https://ge.androidx.dev/cache/"
         var buildCachePassword = System.getenv("GRADLE_BUILD_CACHE_PASSWORD")
-        if (buildCachePassword != null && !buildCachePassword.empty) {
+        if (isCI && buildCachePassword != null && !buildCachePassword.empty) {
             push = true
             credentials {
                 username = "ci"
diff --git a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleEnterpriseConventionsPlugin.kt b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleEnterpriseConventionsPlugin.kt
index abda785..aa5351f 100644
--- a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleEnterpriseConventionsPlugin.kt
+++ b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleEnterpriseConventionsPlugin.kt
@@ -47,6 +47,13 @@
             }
         }
 
+        settings.buildCache.local { local ->
+            // Aggressively clean up stale build cache entries on CI
+            if (isCI) {
+                local.removeUnusedEntriesAfterDays = 1
+            }
+        }
+
         settings.buildCache.remote(HttpBuildCache::class.java) { remote ->
             remote.url = URI("https://ge.androidx.dev/cache/")
             val buildCachePassword = System.getenv("GRADLE_BUILD_CACHE_PASSWORD")
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index 21427a9..0329de3 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -25,7 +25,7 @@
 kotlin.code.style=official
 # Disable docs
 androidx.enableDocumentation=false
-androidx.playground.snapshotBuildId=8138446
+androidx.playground.snapshotBuildId=8145911
 androidx.playground.metalavaBuildId=8073933
 androidx.playground.dokkaBuildId=7472101
 androidx.studio.type=playground
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/SuspendingQueryTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/SuspendingQueryTest.kt
index 4ac4f3a..89004a2 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/SuspendingQueryTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/SuspendingQueryTest.kt
@@ -264,7 +264,6 @@
             }
 
             try {
-                @Suppress("IMPLICIT_NOTHING_AS_TYPE_PARAMETER")
                 database.withTransaction {
                     booksDao.insertBookSuspend(TestUtil.BOOK_2)
                     throw IOException("Boom!")
@@ -310,7 +309,6 @@
                 )
 
                 try {
-                    @Suppress("IMPLICIT_NOTHING_AS_TYPE_PARAMETER")
                     database.withTransaction {
                         booksDao.insertBookSuspend(TestUtil.BOOK_1.copy(salesCnt = 0))
                         throw IOException("Boom!")
@@ -335,7 +333,6 @@
             try {
                 database.withTransaction {
                     try {
-                        @Suppress("IMPLICIT_NOTHING_AS_TYPE_PARAMETER")
                         database.withTransaction {
                             throw IOException("Boom!")
                         }
@@ -852,7 +849,6 @@
     @Test
     @Suppress("DEPRECATION")
     fun withTransaction_endTransaction_error() {
-        @Suppress("IMPLICIT_NOTHING_AS_TYPE_PARAMETER")
         runBlocking {
             try {
                 database.withTransaction {
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorDatabase.java b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorDatabase.java
deleted file mode 100644
index c5ef6bc..0000000
--- a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorDatabase.java
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Copyright 2020 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.room;
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.database.sqlite.SQLiteTransactionListener;
-import android.os.Build;
-import android.os.CancellationSignal;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
-import androidx.sqlite.db.SupportSQLiteDatabase;
-import androidx.sqlite.db.SupportSQLiteQuery;
-import androidx.sqlite.db.SupportSQLiteStatement;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.Executor;
-
-
-/**
- * Implements {@link SupportSQLiteDatabase} for SQLite queries.
- */
-final class QueryInterceptorDatabase implements SupportSQLiteDatabase {
-
-    private final SupportSQLiteDatabase mDelegate;
-    private final RoomDatabase.QueryCallback mQueryCallback;
-    private final Executor mQueryCallbackExecutor;
-
-    QueryInterceptorDatabase(@NonNull SupportSQLiteDatabase supportSQLiteDatabase,
-            @NonNull RoomDatabase.QueryCallback queryCallback, @NonNull Executor
-            queryCallbackExecutor) {
-        mDelegate = supportSQLiteDatabase;
-        mQueryCallback = queryCallback;
-        mQueryCallbackExecutor = queryCallbackExecutor;
-    }
-
-    @NonNull
-    @Override
-    public SupportSQLiteStatement compileStatement(@NonNull String sql) {
-        return new QueryInterceptorStatement(mDelegate.compileStatement(sql),
-                mQueryCallback, sql, mQueryCallbackExecutor);
-    }
-
-    @Override
-    public void beginTransaction() {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("BEGIN EXCLUSIVE TRANSACTION",
-                Collections.emptyList()));
-        mDelegate.beginTransaction();
-    }
-
-    @Override
-    public void beginTransactionNonExclusive() {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("BEGIN DEFERRED TRANSACTION",
-                Collections.emptyList()));
-        mDelegate.beginTransactionNonExclusive();
-    }
-
-    @Override
-    public void beginTransactionWithListener(@NonNull SQLiteTransactionListener
-            transactionListener) {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("BEGIN EXCLUSIVE TRANSACTION",
-                Collections.emptyList()));
-        mDelegate.beginTransactionWithListener(transactionListener);
-    }
-
-    @Override
-    public void beginTransactionWithListenerNonExclusive(
-            @NonNull SQLiteTransactionListener transactionListener) {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("BEGIN DEFERRED TRANSACTION",
-                Collections.emptyList()));
-        mDelegate.beginTransactionWithListenerNonExclusive(transactionListener);
-    }
-
-    @Override
-    public void endTransaction() {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("END TRANSACTION",
-                Collections.emptyList()));
-        mDelegate.endTransaction();
-    }
-
-    @Override
-    public void setTransactionSuccessful() {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("TRANSACTION SUCCESSFUL",
-                Collections.emptyList()));
-        mDelegate.setTransactionSuccessful();
-    }
-
-    @Override
-    public boolean inTransaction() {
-        return mDelegate.inTransaction();
-    }
-
-    @Override
-    public boolean isDbLockedByCurrentThread() {
-        return mDelegate.isDbLockedByCurrentThread();
-    }
-
-    @Override
-    public boolean yieldIfContendedSafely() {
-        return mDelegate.yieldIfContendedSafely();
-    }
-
-    @Override
-    public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
-        return mDelegate.yieldIfContendedSafely(sleepAfterYieldDelay);
-    }
-
-    @Override
-    public int getVersion() {
-        return mDelegate.getVersion();
-    }
-
-    @Override
-    public void setVersion(int version) {
-        mDelegate.setVersion(version);
-    }
-
-    @Override
-    public long getMaximumSize() {
-        return mDelegate.getMaximumSize();
-    }
-
-    @Override
-    public long setMaximumSize(long numBytes) {
-        return mDelegate.setMaximumSize(numBytes);
-    }
-
-    @Override
-    public long getPageSize() {
-        return mDelegate.getPageSize();
-    }
-
-    @Override
-    public void setPageSize(long numBytes) {
-        mDelegate.setPageSize(numBytes);
-    }
-
-    @NonNull
-    @Override
-    public Cursor query(@NonNull String query) {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(query,
-                Collections.emptyList()));
-        return mDelegate.query(query);
-    }
-
-    @NonNull
-    @Override
-    public Cursor query(@NonNull String query, @NonNull Object[] bindArgs) {
-        List<Object> inputArguments = new ArrayList<>();
-        inputArguments.addAll(Arrays.asList(bindArgs));
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(query,
-                inputArguments));
-        return mDelegate.query(query, bindArgs);
-    }
-
-    @NonNull
-    @Override
-    public Cursor query(@NonNull SupportSQLiteQuery query) {
-        QueryInterceptorProgram queryInterceptorProgram = new QueryInterceptorProgram();
-        query.bindTo(queryInterceptorProgram);
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(query.getSql(),
-                queryInterceptorProgram.getBindArgs()));
-        return mDelegate.query(query);
-    }
-
-    @NonNull
-    @Override
-    public Cursor query(@NonNull SupportSQLiteQuery query,
-            @NonNull CancellationSignal cancellationSignal) {
-        QueryInterceptorProgram queryInterceptorProgram = new QueryInterceptorProgram();
-        query.bindTo(queryInterceptorProgram);
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(query.getSql(),
-                queryInterceptorProgram.getBindArgs()));
-        return mDelegate.query(query);
-    }
-
-    @Override
-    public long insert(@NonNull String table, int conflictAlgorithm, @NonNull ContentValues values)
-            throws SQLException {
-        return mDelegate.insert(table, conflictAlgorithm, values);
-    }
-
-    @Override
-    public int delete(@NonNull String table, @NonNull String whereClause,
-            @NonNull Object[] whereArgs) {
-        return mDelegate.delete(table, whereClause, whereArgs);
-    }
-
-    @Override
-    public int update(@NonNull String table, int conflictAlgorithm, @NonNull ContentValues values,
-            @NonNull String whereClause,
-            @NonNull Object[] whereArgs) {
-        return mDelegate.update(table, conflictAlgorithm, values, whereClause,
-                whereArgs);
-    }
-
-    @Override
-    public void execSQL(@NonNull String sql) throws SQLException {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(sql, new ArrayList<>(0)));
-        mDelegate.execSQL(sql);
-    }
-
-    @Override
-    public void execSQL(@NonNull String sql, @NonNull Object[] bindArgs) throws SQLException {
-        List<Object> inputArguments = new ArrayList<>();
-        inputArguments.addAll(Arrays.asList(bindArgs));
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(sql, inputArguments));
-        mDelegate.execSQL(sql, inputArguments.toArray());
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return mDelegate.isReadOnly();
-    }
-
-    @Override
-    public boolean isOpen() {
-        return mDelegate.isOpen();
-    }
-
-    @Override
-    public boolean needUpgrade(int newVersion) {
-        return mDelegate.needUpgrade(newVersion);
-    }
-
-    @NonNull
-    @Override
-    public String getPath() {
-        return mDelegate.getPath();
-    }
-
-    @Override
-    public void setLocale(@NonNull Locale locale) {
-        mDelegate.setLocale(locale);
-    }
-
-    @Override
-    public void setMaxSqlCacheSize(int cacheSize) {
-        mDelegate.setMaxSqlCacheSize(cacheSize);
-    }
-
-    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
-    @Override
-    public void setForeignKeyConstraintsEnabled(boolean enable) {
-        mDelegate.setForeignKeyConstraintsEnabled(enable);
-    }
-
-    @Override
-    public boolean enableWriteAheadLogging() {
-        return mDelegate.enableWriteAheadLogging();
-    }
-
-    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
-    @Override
-    public void disableWriteAheadLogging() {
-        mDelegate.disableWriteAheadLogging();
-    }
-
-    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
-    @Override
-    public boolean isWriteAheadLoggingEnabled() {
-        return mDelegate.isWriteAheadLoggingEnabled();
-    }
-
-    @NonNull
-    @Override
-    public List<Pair<String, String>> getAttachedDbs() {
-        return mDelegate.getAttachedDbs();
-    }
-
-    @Override
-    public boolean isDatabaseIntegrityOk() {
-        return mDelegate.isDatabaseIntegrityOk();
-    }
-
-    @Override
-    public void close() throws IOException {
-        mDelegate.close();
-    }
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorDatabase.kt b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorDatabase.kt
new file mode 100644
index 0000000..f46fe60
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorDatabase.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2020 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.room
+
+import android.database.Cursor
+import android.database.sqlite.SQLiteTransactionListener
+import android.os.CancellationSignal
+import androidx.annotation.NonNull
+
+import androidx.sqlite.db.SupportSQLiteDatabase
+import androidx.sqlite.db.SupportSQLiteQuery
+import androidx.sqlite.db.SupportSQLiteStatement
+import java.util.concurrent.Executor
+
+/**
+ * Implements [SupportSQLiteDatabase] for SQLite queries.
+ */
+internal class QueryInterceptorDatabase(
+    private val delegate: SupportSQLiteDatabase,
+    private val queryCallbackExecutor: Executor,
+    private val queryCallback: RoomDatabase.QueryCallback
+) : SupportSQLiteDatabase by delegate {
+
+   override fun compileStatement(sql: String): SupportSQLiteStatement {
+        return QueryInterceptorStatement(
+            delegate.compileStatement(sql),
+            sql,
+            queryCallbackExecutor,
+            queryCallback,
+        )
+    }
+
+    override fun beginTransaction() {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery("BEGIN EXCLUSIVE TRANSACTION", emptyList())
+        }
+        delegate.beginTransaction()
+    }
+
+    override fun beginTransactionNonExclusive() {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery("BEGIN DEFERRED TRANSACTION", emptyList())
+        }
+        delegate.beginTransactionNonExclusive()
+    }
+
+    override fun beginTransactionWithListener(transactionListener: SQLiteTransactionListener) {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery("BEGIN EXCLUSIVE TRANSACTION", emptyList())
+        }
+        delegate.beginTransactionWithListener(transactionListener)
+    }
+
+    override fun beginTransactionWithListenerNonExclusive(
+        transactionListener: SQLiteTransactionListener
+    ) {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery("BEGIN DEFERRED TRANSACTION", emptyList())
+        }
+        delegate.beginTransactionWithListenerNonExclusive(transactionListener)
+    }
+
+   override fun endTransaction() {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery("END TRANSACTION", emptyList())
+        }
+       delegate.endTransaction()
+    }
+
+    override fun setTransactionSuccessful() {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery("TRANSACTION SUCCESSFUL", emptyList())
+        }
+        delegate.setTransactionSuccessful()
+    }
+
+    override fun query(query: String): Cursor {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery(query, emptyList())
+        }
+        return delegate.query(query)
+    }
+
+    override fun query(query: String, vararg bindArgs: Any): Cursor {
+        queryCallbackExecutor.execute { queryCallback.onQuery(query, bindArgs.toList()) }
+        return delegate.query(query, bindArgs)
+    }
+
+    override fun query(query: SupportSQLiteQuery): Cursor {
+        val queryInterceptorProgram = QueryInterceptorProgram()
+        query.bindTo(queryInterceptorProgram)
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery(query.getSql(), queryInterceptorProgram.bindArgsCache)
+        }
+        return delegate.query(query)
+    }
+
+    @NonNull
+    override fun query(
+        @NonNull query: SupportSQLiteQuery,
+        @NonNull cancellationSignal: CancellationSignal?
+    ): Cursor {
+        val queryInterceptorProgram = QueryInterceptorProgram()
+        query.bindTo(queryInterceptorProgram)
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery(
+                query.sql,
+                queryInterceptorProgram.bindArgsCache
+            )
+        }
+        return delegate.query(query)
+    }
+
+    // Suppress warning about `SQL` in execSQL not being camel case. This is an override function
+    // and it can't be renamed.
+    @Suppress("AcronymName")
+    override fun execSQL(sql: String) {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery(sql, emptyList())
+        }
+        delegate.execSQL(sql)
+    }
+
+    // Suppress warning about `SQL` in execSQL not being camel case. This is an override function
+    // and it can't be renamed.
+    @Suppress("AcronymName")
+    override fun execSQL(sql: String, vararg bindArgs: Any) {
+        val inputArguments = mutableListOf<Any>()
+        inputArguments.addAll(listOf(bindArgs))
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery(sql, inputArguments)
+        }
+        delegate.execSQL(sql, arrayOf(inputArguments))
+    }
+}
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelper.java b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelper.java
deleted file mode 100644
index 9916294..0000000
--- a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelper.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2020 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.room;
-
-import android.os.Build;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.sqlite.db.SupportSQLiteDatabase;
-import androidx.sqlite.db.SupportSQLiteOpenHelper;
-
-import java.util.concurrent.Executor;
-
-final class QueryInterceptorOpenHelper implements SupportSQLiteOpenHelper, DelegatingOpenHelper {
-
-    private final SupportSQLiteOpenHelper mDelegate;
-    private final RoomDatabase.QueryCallback mQueryCallback;
-    private final Executor mQueryCallbackExecutor;
-
-    QueryInterceptorOpenHelper(@NonNull SupportSQLiteOpenHelper supportSQLiteOpenHelper,
-            @NonNull RoomDatabase.QueryCallback queryCallback, @NonNull Executor
-            queryCallbackExecutor) {
-        mDelegate = supportSQLiteOpenHelper;
-        mQueryCallback = queryCallback;
-        mQueryCallbackExecutor = queryCallbackExecutor;
-    }
-
-    @Nullable
-    @Override
-    public String getDatabaseName() {
-        return mDelegate.getDatabaseName();
-    }
-
-    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
-    @Override
-    public void setWriteAheadLoggingEnabled(boolean enabled) {
-        mDelegate.setWriteAheadLoggingEnabled(enabled);
-    }
-
-    @Override
-    public SupportSQLiteDatabase getWritableDatabase() {
-        return new QueryInterceptorDatabase(mDelegate.getWritableDatabase(), mQueryCallback,
-                mQueryCallbackExecutor);
-    }
-
-    @Override
-    public SupportSQLiteDatabase getReadableDatabase() {
-        return new QueryInterceptorDatabase(mDelegate.getReadableDatabase(), mQueryCallback,
-                mQueryCallbackExecutor);
-    }
-
-    @Override
-    public void close() {
-        mDelegate.close();
-    }
-
-    @Override
-    @NonNull
-    public SupportSQLiteOpenHelper getDelegate() {
-        return mDelegate;
-    }
-
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelper.kt b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelper.kt
new file mode 100644
index 0000000..e64ba26
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelper.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2020 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.room
+
+import androidx.sqlite.db.SupportSQLiteDatabase
+import androidx.sqlite.db.SupportSQLiteOpenHelper
+
+import java.util.concurrent.Executor
+
+internal class QueryInterceptorOpenHelper(
+    private val delegate: SupportSQLiteOpenHelper,
+    private val queryCallbackExecutor: Executor,
+    private val queryCallback: RoomDatabase.QueryCallback
+) : SupportSQLiteOpenHelper by delegate, DelegatingOpenHelper {
+    override fun getWritableDatabase(): SupportSQLiteDatabase {
+        return QueryInterceptorDatabase(
+            delegate.writableDatabase,
+            queryCallbackExecutor,
+            queryCallback
+        )
+    }
+
+    override fun getReadableDatabase(): SupportSQLiteDatabase {
+        return QueryInterceptorDatabase(
+            delegate.readableDatabase,
+            queryCallbackExecutor,
+            queryCallback
+        )
+    }
+
+   override fun getDelegate(): SupportSQLiteOpenHelper {
+        return delegate
+    }
+}
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelperFactory.java b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelperFactory.java
deleted file mode 100644
index 5d94cd1..0000000
--- a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelperFactory.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2020 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.room;
-
-import androidx.annotation.NonNull;
-import androidx.sqlite.db.SupportSQLiteOpenHelper;
-
-import java.util.concurrent.Executor;
-
-/**
- * Implements {@link SupportSQLiteOpenHelper.Factory} to wrap QueryInterceptorOpenHelper.
- */
-@SuppressWarnings("AcronymName")
-final class QueryInterceptorOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
-
-    private final SupportSQLiteOpenHelper.Factory mDelegate;
-    private final RoomDatabase.QueryCallback mQueryCallback;
-    private final Executor mQueryCallbackExecutor;
-
-    @SuppressWarnings("LambdaLast")
-    QueryInterceptorOpenHelperFactory(@NonNull SupportSQLiteOpenHelper.Factory factory,
-            @NonNull RoomDatabase.QueryCallback queryCallback,
-            @NonNull Executor queryCallbackExecutor) {
-        mDelegate = factory;
-        mQueryCallback = queryCallback;
-        mQueryCallbackExecutor = queryCallbackExecutor;
-    }
-
-    @NonNull
-    @Override
-    public SupportSQLiteOpenHelper create(
-            @NonNull SupportSQLiteOpenHelper.Configuration configuration) {
-        return new QueryInterceptorOpenHelper(mDelegate.create(configuration), mQueryCallback,
-                mQueryCallbackExecutor);
-    }
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelperFactory.kt b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelperFactory.kt
new file mode 100644
index 0000000..d0fab60
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorOpenHelperFactory.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 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.room
+
+import androidx.sqlite.db.SupportSQLiteOpenHelper
+
+import java.util.concurrent.Executor
+
+/**
+ * Implements [SupportSQLiteOpenHelper.Factory] to wrap [QueryInterceptorOpenHelper].
+ */
+internal class QueryInterceptorOpenHelperFactory(
+    private val delegate: SupportSQLiteOpenHelper.Factory,
+    private val queryCallbackExecutor: Executor,
+    private val queryCallback: RoomDatabase.QueryCallback,
+) : SupportSQLiteOpenHelper.Factory by delegate {
+    override fun create(
+        configuration: SupportSQLiteOpenHelper.Configuration
+    ): SupportSQLiteOpenHelper {
+        return QueryInterceptorOpenHelper(
+            delegate.create(configuration),
+            queryCallbackExecutor,
+            queryCallback
+        )
+    }
+}
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorProgram.java b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorProgram.java
deleted file mode 100644
index 2b9c554..0000000
--- a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorProgram.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2020 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.room;
-
-import androidx.sqlite.db.SupportSQLiteProgram;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A program implementing an {@link SupportSQLiteProgram} API to record bind arguments.
- */
-final class QueryInterceptorProgram implements SupportSQLiteProgram {
-    private List<Object> mBindArgsCache = new ArrayList<>();
-
-    @Override
-    public void bindNull(int index) {
-        saveArgsToCache(index, null);
-    }
-
-    @Override
-    public void bindLong(int index, long value) {
-        saveArgsToCache(index, value);
-    }
-
-    @Override
-    public void bindDouble(int index, double value) {
-        saveArgsToCache(index, value);
-    }
-
-    @Override
-    public void bindString(int index, String value) {
-        saveArgsToCache(index, value);
-    }
-
-    @Override
-    public void bindBlob(int index, byte[] value) {
-        saveArgsToCache(index, value);
-    }
-
-    @Override
-    public void clearBindings() {
-        mBindArgsCache.clear();
-    }
-
-    @Override
-    public void close() { }
-
-    private void saveArgsToCache(int bindIndex, Object value) {
-        // The index into bind methods are 1...n
-        int index = bindIndex - 1;
-        if (index >= mBindArgsCache.size()) {
-            for (int i = mBindArgsCache.size(); i <= index; i++) {
-                mBindArgsCache.add(null);
-            }
-        }
-        mBindArgsCache.set(index, value);
-    }
-
-    /**
-     * Returns the list of arguments associated with the query.
-     *
-     * @return argument list.
-     */
-    List<Object> getBindArgs() {
-        return mBindArgsCache;
-    }
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorProgram.kt b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorProgram.kt
new file mode 100644
index 0000000..281df01
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorProgram.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2020 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.room
+
+import androidx.sqlite.db.SupportSQLiteProgram
+
+/**
+ * A program implementing an [SupportSQLiteProgram] API to record bind arguments.
+ */
+internal class QueryInterceptorProgram : SupportSQLiteProgram {
+    internal val bindArgsCache = mutableListOf<Any?>()
+
+    override fun bindNull(index: Int) {
+        saveArgsToCache(index, null)
+    }
+
+    override fun bindLong(index: Int, value: Long) {
+        saveArgsToCache(index, value)
+    }
+
+    override fun bindDouble(index: Int, value: Double) {
+        saveArgsToCache(index, value)
+    }
+
+    override fun bindString(index: Int, value: String?) {
+        saveArgsToCache(index, value)
+    }
+
+    override fun bindBlob(index: Int, value: ByteArray?) {
+        saveArgsToCache(index, value)
+    }
+
+    override fun clearBindings() {
+        bindArgsCache.clear()
+    }
+
+    override fun close() {}
+
+    private fun saveArgsToCache(bindIndex: Int, value: Any?) {
+        // The index into bind methods are 1...n
+        val index = bindIndex - 1
+        if (index >= bindArgsCache.size) {
+            for (i in bindArgsCache.size..index) {
+                bindArgsCache.add(null)
+            }
+        }
+        bindArgsCache[index] = value
+    }
+}
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.java b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.java
deleted file mode 100644
index 8825252..0000000
--- a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright 2020 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.room;
-
-import androidx.annotation.NonNull;
-import androidx.sqlite.db.SupportSQLiteStatement;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Implements an instance of {@link SupportSQLiteStatement} for SQLite queries.
- */
-final class QueryInterceptorStatement implements SupportSQLiteStatement {
-
-    private final SupportSQLiteStatement mDelegate;
-    private final RoomDatabase.QueryCallback mQueryCallback;
-    private final String mSqlStatement;
-    private final List<Object> mBindArgsCache = new ArrayList<>();
-    private final Executor mQueryCallbackExecutor;
-
-    QueryInterceptorStatement(@NonNull SupportSQLiteStatement compileStatement,
-            @NonNull RoomDatabase.QueryCallback queryCallback, String sqlStatement,
-            @NonNull Executor queryCallbackExecutor) {
-        mDelegate = compileStatement;
-        mQueryCallback = queryCallback;
-        mSqlStatement = sqlStatement;
-        mQueryCallbackExecutor = queryCallbackExecutor;
-    }
-
-    @Override
-    public void execute() {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(mSqlStatement, mBindArgsCache));
-        mDelegate.execute();
-    }
-
-    @Override
-    public int executeUpdateDelete() {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(mSqlStatement, mBindArgsCache));
-        return mDelegate.executeUpdateDelete();
-    }
-
-    @Override
-    public long executeInsert() {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(mSqlStatement, mBindArgsCache));
-        return mDelegate.executeInsert();
-    }
-
-    @Override
-    public long simpleQueryForLong() {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(mSqlStatement, mBindArgsCache));
-        return mDelegate.simpleQueryForLong();
-    }
-
-    @Override
-    public String simpleQueryForString() {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(mSqlStatement, mBindArgsCache));
-        return mDelegate.simpleQueryForString();
-    }
-
-    @Override
-    public void bindNull(int index) {
-        saveArgsToCache(index, mBindArgsCache.toArray());
-        mDelegate.bindNull(index);
-    }
-
-    @Override
-    public void bindLong(int index, long value) {
-        saveArgsToCache(index, value);
-        mDelegate.bindLong(index, value);
-    }
-
-    @Override
-    public void bindDouble(int index, double value) {
-        saveArgsToCache(index, value);
-        mDelegate.bindDouble(index, value);
-    }
-
-    @Override
-    public void bindString(int index, String value) {
-        saveArgsToCache(index, value);
-        mDelegate.bindString(index, value);
-    }
-
-    @Override
-    public void bindBlob(int index, byte[] value) {
-        saveArgsToCache(index, value);
-        mDelegate.bindBlob(index, value);
-    }
-
-    @Override
-    public void clearBindings() {
-        mBindArgsCache.clear();
-        mDelegate.clearBindings();
-    }
-
-    @Override
-    public void close() throws IOException {
-        mDelegate.close();
-    }
-
-    private void saveArgsToCache(int bindIndex, Object value) {
-        int index = bindIndex - 1;
-        if (index >= mBindArgsCache.size()) {
-            // Add null entries to the list until we have the desired # of indices
-            for (int i = mBindArgsCache.size(); i <= index; i++) {
-                mBindArgsCache.add(null);
-            }
-        }
-        mBindArgsCache.set(index, value);
-    }
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.kt b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.kt
new file mode 100644
index 0000000..a182885
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 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.room
+
+import androidx.sqlite.db.SupportSQLiteStatement
+
+import java.util.concurrent.Executor
+
+/**
+ * Implements an instance of [SupportSQLiteStatement] for SQLite queries.
+ */
+internal class QueryInterceptorStatement(
+    private val delegate: SupportSQLiteStatement,
+    private val sqlStatement: String,
+    private val queryCallbackExecutor: Executor,
+    private val queryCallback: RoomDatabase.QueryCallback,
+) : SupportSQLiteStatement by delegate {
+
+    private val bindArgsCache = mutableListOf<Any?>()
+
+    override fun execute() {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery(sqlStatement, bindArgsCache)
+        }
+        delegate.execute()
+    }
+
+    override fun executeUpdateDelete(): Int {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery(sqlStatement, bindArgsCache)
+        }
+        return delegate.executeUpdateDelete()
+    }
+
+   override fun executeInsert(): Long {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery(sqlStatement, bindArgsCache)
+        }
+        return delegate.executeInsert()
+    }
+
+    override fun simpleQueryForLong(): Long {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery(sqlStatement, bindArgsCache)
+        }
+        return delegate.simpleQueryForLong()
+    }
+
+    override fun simpleQueryForString(): String {
+        queryCallbackExecutor.execute {
+            queryCallback.onQuery(sqlStatement, bindArgsCache)
+        }
+        return delegate.simpleQueryForString()
+    }
+
+    override fun bindNull(index: Int) {
+        saveArgsToCache(index, arrayOf(*bindArgsCache.toTypedArray()))
+        delegate.bindNull(index)
+    }
+
+    override fun bindLong(index: Int, value: Long) {
+        saveArgsToCache(index, value)
+        delegate.bindLong(index, value)
+    }
+
+    override fun bindDouble(index: Int, value: Double) {
+        saveArgsToCache(index, value)
+        delegate.bindDouble(index, value)
+    }
+
+    override fun bindString(index: Int, value: String?) {
+        saveArgsToCache(index, value)
+        delegate.bindString(index, value)
+    }
+
+    override fun bindBlob(index: Int, value: ByteArray?) {
+        saveArgsToCache(index, value)
+        delegate.bindBlob(index, value)
+    }
+
+    override fun clearBindings() {
+        bindArgsCache.clear()
+        delegate.clearBindings()
+    }
+
+    private fun saveArgsToCache(bindIndex: Int, value: Any?) {
+        val index = bindIndex - 1
+        if (index >= bindArgsCache.size) {
+            // Add null entries to the list until we have the desired # of indices
+            repeat(index - bindArgsCache.size + 1) {
+                bindArgsCache.add(null)
+            }
+        }
+        bindArgsCache[index] = value
+    }
+}
diff --git a/room/room-runtime/src/main/java/androidx/room/RoomDatabase.java b/room/room-runtime/src/main/java/androidx/room/RoomDatabase.java
index 54427d6..16afcb6 100644
--- a/room/room-runtime/src/main/java/androidx/room/RoomDatabase.java
+++ b/room/room-runtime/src/main/java/androidx/room/RoomDatabase.java
@@ -1458,8 +1458,11 @@
             }
 
             if (mQueryCallback != null) {
-                factory = new QueryInterceptorOpenHelperFactory(factory, mQueryCallback,
-                        mQueryCallbackExecutor);
+                factory = new QueryInterceptorOpenHelperFactory(
+                        factory,
+                        mQueryCallbackExecutor,
+                        mQueryCallback
+                );
             }
 
             DatabaseConfiguration configuration =
diff --git a/viewpager2/integration-tests/testapp/lint-baseline.xml b/viewpager2/integration-tests/testapp/lint-baseline.xml
index c6d03d2..2261b3c 100644
--- a/viewpager2/integration-tests/testapp/lint-baseline.xml
+++ b/viewpager2/integration-tests/testapp/lint-baseline.xml
@@ -1,5 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.2.0-dev" type="baseline" client="gradle" dependencies="false" name="AGP (7.2.0-dev)" variant="all" version="7.2.0-dev">
+<issues format="6" by="lint 7.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (7.3.0-alpha01)" variant="all" version="7.3.0-alpha01">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 19 (current min is 14): `java.lang.AssertionError()`"
+        errorLine1="                throw AssertionError(&quot;Block hit bad state $n times&quot;, e)"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidTest/java/androidx/viewpager2/integration/testapp/test/util/RetryBlock.kt"
+            line="50"
+            column="23"/>
+    </issue>
 
     <issue
         id="ClassVerificationFailure"
@@ -23,26 +34,4 @@
             column="25"/>
     </issue>
 
-    <issue
-        id="UnknownNullness"
-        message="Should explicitly declare type here since implicit type does not specify nullness"
-        errorLine1="    fun removeAt(position: Int) = items.removeAt(position)"
-        errorLine2="        ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/integration/testapp/MutableCollectionBaseActivity.kt"
-            line="146"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Should explicitly declare type here since implicit type does not specify nullness"
-        errorLine1="        fun create(itemText: String) ="
-        errorLine2="            ~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/integration/testapp/MutableCollectionFragmentActivity.kt"
-            line="98"
-            column="13"/>
-    </issue>
-
 </issues>
diff --git a/viewpager2/viewpager2/lint-baseline.xml b/viewpager2/viewpager2/lint-baseline.xml
index 045f63a..3b2356d 100644
--- a/viewpager2/viewpager2/lint-baseline.xml
+++ b/viewpager2/viewpager2/lint-baseline.xml
@@ -1,5 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (7.1.0-beta02)" variant="all" version="7.1.0-beta02">
+<issues format="6" by="lint 7.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (7.3.0-alpha01)" variant="all" version="7.3.0-alpha01">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 19 (current min is 14): `java.lang.AssertionError()`"
+        errorLine1="                throw AssertionError(&quot;Block hit bad state $n times&quot;, e)"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt"
+            line="737"
+            column="23"/>
+    </issue>
 
     <issue
         id="NewApi"
@@ -19,7 +30,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="579"
+            line="582"
             column="16"/>
     </issue>
 
@@ -30,7 +41,7 @@
         errorLine2="        ~~~~~">
         <location
             file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="181"
+            line="183"
             column="9"/>
     </issue>
 
@@ -41,129 +52,8 @@
         errorLine2="            ~~~~~">
         <location
             file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="391"
+            line="393"
             column="13"/>
     </issue>
 
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.viewpager2.widget.ViewPager2 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        if (applied.isConsumed()) {"
-        errorLine2="                    ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="972"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 20; however, the containing class androidx.viewpager2.widget.ViewPager2 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mRecyclerView.getChildAt(i).dispatchApplyWindowInsets(new WindowInsets(applied));"
-        errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="985"
-            column="41"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 20; however, the containing class androidx.viewpager2.widget.ViewPager2 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mRecyclerView.getChildAt(i).dispatchApplyWindowInsets(new WindowInsets(applied));"
-        errorLine2="                                                                  ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="985"
-            column="67"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 20; however, the containing class androidx.viewpager2.widget.ViewPager2 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return insets.consumeSystemWindowInsets().consumeStableInsets();"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="1008"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.viewpager2.widget.ViewPager2 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return insets.consumeSystemWindowInsets().consumeStableInsets();"
-        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="1008"
-            column="51"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public CharSequence getAccessibilityClassName() {"
-        errorLine2="           ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="291"
-            column="12"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    protected void onRestoreInstanceState(Parcelable state) {"
-        errorLine2="                                          ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="333"
-            column="43"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    protected void dispatchRestoreInstanceState(SparseArray&lt;Parcelable> container) {"
-        errorLine2="                                                ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="368"
-            column="49"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public void onViewAdded(View child) {"
-        errorLine2="                            ~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="492"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {"
-        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="952"
-            column="51"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public boolean performAccessibilityAction(int action, Bundle arguments) {"
-        errorLine2="                                                          ~~~~~~">
-        <location
-            file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"
-            line="959"
-            column="59"/>
-    </issue>
-
 </issues>
diff --git a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfoTest.kt b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfoTest.kt
index 43d5ce2..051684e 100644
--- a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfoTest.kt
+++ b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfoTest.kt
@@ -67,6 +67,7 @@
         }
     }
 
+    @FlakyTest(bugId = 217762753)
     @Test
     fun visibleItemsAreCorrect() {
         lateinit var state: ScalingLazyListState
@@ -93,6 +94,7 @@
         }
     }
 
+    @FlakyTest(bugId = 217762274)
     @Test
     fun visibleItemsAreCorrectSetExplicitInitialItemIndex() {
         lateinit var state: ScalingLazyListState
diff --git a/wear/tiles/tiles-material/lint-baseline.xml b/wear/tiles/tiles-material/lint-baseline.xml
new file mode 100644
index 0000000..bf58691
--- /dev/null
+++ b/wear/tiles/tiles-material/lint-baseline.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 7.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (7.3.0-alpha01)" variant="all" version="7.3.0-alpha01">
+
+    <issue
+        id="ResourceType"
+        message="Mismatched @Dimension units here; expected a pixel integer but received density-independent (dp) integer"
+        errorLine1="            mSize = dp(size);"
+        errorLine2="                       ~~~~">
+        <location
+            file="src/main/java/androidx/wear/tiles/material/Button.java"
+            line="151"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="ResourceType"
+        message="Mismatched @Dimension units here; expected density-independent (dp) integer but received a pixel integer"
+        errorLine1="        return recommendedIconSize(buttonSize.getValue());"
+        errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/wear/tiles/material/ButtonDefaults.java"
+            line="46"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="ResourceType"
+        message="Mismatched @Dimension units here; expected a pixel integer but received density-independent (dp) integer"
+        errorLine1="            mWidth = dp(width);"
+        errorLine2="                        ~~~~~">
+        <location
+            file="src/main/java/androidx/wear/tiles/material/Chip.java"
+            line="160"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="ResourceType"
+        message="Mismatched @Dimension units here; expected a pixel integer but received density-independent (dp) integer"
+        errorLine1="            this.mStrokeWidth = dp(strokeWidth);"
+        errorLine2="                                   ~~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/wear/tiles/material/CircularProgressIndicator.java"
+            line="162"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="ResourceType"
+        message="Mismatched @Dimension units here; expected a pixel integer but received density-independent (dp) integer"
+        errorLine1="            this.mHorizontalSpacerWidth = dp(width);"
+        errorLine2="                                             ~~~~~">
+        <location
+            file="src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java"
+            line="142"
+            column="46"/>
+    </issue>
+
+    <issue
+        id="ResourceType"
+        message="Mismatched @Dimension units here; expected a pixel integer but received density-independent (dp) integer"
+        errorLine1="            this.mVerticalSpacerHeight = dp(height);"
+        errorLine2="                                            ~~~~~~">
+        <location
+            file="src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java"
+            line="157"
+            column="45"/>
+    </issue>
+
+    <issue
+        id="ResourceType"
+        message="Mismatched @Dimension units here; expected a pixel integer but received density-independent (dp) integer"
+        errorLine1="            mWidth = dp(width);"
+        errorLine2="                        ~~~~~">
+        <location
+            file="src/main/java/androidx/wear/tiles/material/TitleChip.java"
+            line="129"
+            column="25"/>
+    </issue>
+
+</issues>
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java
index e2ff6c7..440e987 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java
@@ -181,8 +181,10 @@
          * ButtonColors} and with the given size. This icon should be image with chosen alpha
          * channel and not an actual image.
          */
+        // There are multiple methods to set different type of content, but there is general getter
+        // getContent that will return LayoutElement set by any of them. b/217197259
         @NonNull
-        @SuppressWarnings("MissingGetterMatchingBuilder")   // There's getContent() method.
+        @SuppressWarnings("MissingGetterMatchingBuilder")
         public Builder setIconContent(@NonNull String resourceId, @NonNull DpProp size) {
             resetContent();
             this.mIcon = resourceId;
@@ -198,8 +200,10 @@
          * icon will be tinted to the given content color from {@link ButtonColors}. This icon
          * should be image with chosen alpha channel and not an actual image.
          */
+        // There are multiple methods to set different type of content, but there is general getter
+        // getContent that will return LayoutElement set by any of them. b/217197259
         @NonNull
-        @SuppressWarnings("MissingGetterMatchingBuilder")   // There's getContent() method.
+        @SuppressWarnings("MissingGetterMatchingBuilder")
         public Builder setIconContent(@NonNull String resourceId) {
             resetContent();
             this.mIcon = resourceId;
@@ -216,8 +220,10 @@
          * respectively). Any previously added content will be overridden. Text should contain no
          * more than 3 characters, otherwise it will overflow from the edges.
          */
+        // There are multiple methods to set different type of content, but there is general getter
+        // getContent that will return LayoutElement set by any of them. b/217197259
         @NonNull
-        @SuppressWarnings("MissingGetterMatchingBuilder")   // There's getContent() method.
+        @SuppressWarnings("MissingGetterMatchingBuilder")
         public Builder setTextContent(
                 @NonNull String text, @NonNull DeviceParameters deviceParameters) {
             resetContent();
@@ -235,8 +241,10 @@
          * customizing the colors of the button. Any previously added content will be overridden.
          * Text should contain no more than 3 characters, otherwise it will overflow from the edges.
          */
+        // There are multiple methods to set different type of content, but there is general getter
+        // getContent that will return LayoutElement set by any of them. b/217197259
         @NonNull
-        @SuppressWarnings("MissingGetterMatchingBuilder")   // There's getContent() method.
+        @SuppressWarnings("MissingGetterMatchingBuilder")
         public Builder setTextContent(@NonNull String text, @NonNull FontStyle font) {
             resetContent();
             this.mText = text;
@@ -252,8 +260,10 @@
          * will be tinted to the given content color from {@link ButtonColors}. This icon should be
          * image with chosen alpha channel and not an actual image.
          */
+        // There are multiple methods to set different type of content, but there is general getter
+        // getContent that will return LayoutElement set by any of them. b/217197259
         @NonNull
-        @SuppressWarnings("MissingGetterMatchingBuilder")   // There's getContent() method.
+        @SuppressWarnings("MissingGetterMatchingBuilder")
         public Builder setImageContent(@NonNull String resourceId) {
             resetContent();
             this.mImage = resourceId;
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java
index 6108c03..0391a36 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java
@@ -187,9 +187,9 @@
          * content will be overridden. Primary text can be on 1 or 2 lines, depending on the length.
          */
         // There are multiple methods to set different type of content, but there is general getter
-        // getContent that will return LayoutElement set by any of it. b/217197259
+        // getContent that will return LayoutElement set by any of them. b/217197259
         @NonNull
-        @SuppressWarnings("MissingGetterMatchingBuilder") // There's getContent() method.
+        @SuppressWarnings("MissingGetterMatchingBuilder")
         public Builder setPrimaryTextContent(@NonNull String primaryText) {
             this.mPrimaryText = primaryText;
             this.mLabelText = null;
@@ -213,8 +213,10 @@
          * Any previously added content will be overridden. Primary text can be shown on 1 line
          * only.
          */
+        // There are multiple methods to set different type of content, but there is general getter
+        // getContent that will return LayoutElement set by any of them. b/217197259
         @NonNull
-        @SuppressWarnings("MissingGetterMatchingBuilder") // There's getContent() method.
+        @SuppressWarnings("MissingGetterMatchingBuilder")
         public Builder setPrimaryTextLabelContent(
                 @NonNull String primaryText, @NonNull String label) {
             this.mPrimaryText = primaryText;
@@ -229,8 +231,10 @@
          * tinted to the given content color from {@link ChipColors}. This icon should be image with
          * chosen alpha channel and not an actual image.
          */
+        // There are multiple methods to set different type of content, but there is general getter
+        // getContent that will return LayoutElement set by any of them. b/217197259
         @NonNull
-        @SuppressWarnings("MissingGetterMatchingBuilder") // There's getContent() method.
+        @SuppressWarnings("MissingGetterMatchingBuilder")
         public Builder setPrimaryTextIconContent(
                 @NonNull String primaryText, @NonNull String resourceId) {
             this.mPrimaryText = primaryText;
@@ -246,8 +250,10 @@
          * content color from {@link ChipColors}. This icon should be image with chosen alpha
          * channel and not an actual image. Primary text can be shown on 1 line only.
          */
+        // There are multiple methods to set different type of content, but there is general getter
+        // getContent that will return LayoutElement set by any of them. b/217197259
         @NonNull
-        @SuppressWarnings("MissingGetterMatchingBuilder") // There's getContent() method.
+        @SuppressWarnings("MissingGetterMatchingBuilder")
         public Builder setPrimaryTextLabelIconContent(
                 @NonNull String primaryText, @NonNull String label, @NonNull String resourceId) {
             this.mPrimaryText = primaryText;
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java
index bee4533..f7ddb6d 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java
@@ -56,7 +56,7 @@
  * same value, with the {@link LayoutDefaults#MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH} space
  * between.
  */
-// TODO(b/215323986)
+// TODO(b/215323986): Link visuals.
 public class MultiSlotLayout implements LayoutElement {
     @NonNull private final PrimaryLayout mElement;
 
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java
index f5edb8e..58c2652 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java
@@ -53,7 +53,7 @@
  * (compact) chip at the bottom with the given content in a center and the recommended margin and
  * padding applied.
  */
-// TODO(b/215323986)
+// TODO(b/215323986): Link visuals.
 public class PrimaryLayout implements LayoutElement {
     @NonNull private final LayoutElement mElement;
 
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/ProgressIndicatorLayout.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/ProgressIndicatorLayout.java
index b30b7b8..0497de9 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/ProgressIndicatorLayout.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/ProgressIndicatorLayout.java
@@ -46,7 +46,7 @@
  * indicator around the edges of the screen and the given content inside of it and the recommended
  * margin and padding applied.
  */
-// TODO(b/215323986)
+// TODO(b/215323986): Link visuals.
 public class ProgressIndicatorLayout implements LayoutElement {
     @NonNull private final LayoutElement mElement;
 
diff --git a/wear/watchface/watchface-complications-data-source-samples/build.gradle b/wear/watchface/watchface-complications-data-source-samples/build.gradle
index f0eb9fb..501ba62 100644
--- a/wear/watchface/watchface-complications-data-source-samples/build.gradle
+++ b/wear/watchface/watchface-complications-data-source-samples/build.gradle
@@ -16,7 +16,7 @@
 
 plugins {
     id("AndroidXPlugin")
-    id("com.android.application")
+    id("com.android.library")
     id("kotlin-android")
 }
 
diff --git a/wear/watchface/watchface-data/src/main/AndroidManifest.xml b/wear/watchface/watchface-data/src/main/AndroidManifest.xml
index 2be667d..d3ca6a5 100644
--- a/wear/watchface/watchface-data/src/main/AndroidManifest.xml
+++ b/wear/watchface/watchface-data/src/main/AndroidManifest.xml
@@ -16,5 +16,5 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.support.wearable.watchface">
+    package="androidx.wear.watchface.data">
 </manifest>
diff --git a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/accessibility/AccessibilityUtils.java b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/accessibility/AccessibilityUtils.java
index ab96188..054835b 100644
--- a/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/accessibility/AccessibilityUtils.java
+++ b/wear/watchface/watchface-data/src/main/java/android/support/wearable/watchface/accessibility/AccessibilityUtils.java
@@ -21,11 +21,11 @@
 import android.support.wearable.complications.ComplicationText;
 import android.support.wearable.complications.ComplicationTextTemplate;
 import android.support.wearable.complications.TimeDependentText;
-import android.support.wearable.watchface.R;
 import android.text.format.DateFormat;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
+import androidx.wear.watchface.data.R;
 
 /**
  * Utilities for making watch faces and complications accessible.
diff --git a/wear/watchface/watchface/api/current.txt b/wear/watchface/watchface/api/current.txt
index 6f138d1..d241577 100644
--- a/wear/watchface/watchface/api/current.txt
+++ b/wear/watchface/watchface/api/current.txt
@@ -144,8 +144,10 @@
     method public androidx.wear.watchface.RenderParameters.HighlightLayer? getHighlightLayer();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> getLastComplicationTapDownEvents();
     method public java.util.Set<androidx.wear.watchface.style.WatchFaceLayer> getWatchFaceLayers();
+    method public boolean isForScreenshot();
     property public final androidx.wear.watchface.DrawMode drawMode;
     property public final androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer;
+    property public final boolean isForScreenshot;
     property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents;
     property public final java.util.Set<androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers;
     field public static final androidx.wear.watchface.RenderParameters.Companion Companion;
diff --git a/wear/watchface/watchface/api/public_plus_experimental_current.txt b/wear/watchface/watchface/api/public_plus_experimental_current.txt
index 6f138d1..d241577 100644
--- a/wear/watchface/watchface/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface/api/public_plus_experimental_current.txt
@@ -144,8 +144,10 @@
     method public androidx.wear.watchface.RenderParameters.HighlightLayer? getHighlightLayer();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> getLastComplicationTapDownEvents();
     method public java.util.Set<androidx.wear.watchface.style.WatchFaceLayer> getWatchFaceLayers();
+    method public boolean isForScreenshot();
     property public final androidx.wear.watchface.DrawMode drawMode;
     property public final androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer;
+    property public final boolean isForScreenshot;
     property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents;
     property public final java.util.Set<androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers;
     field public static final androidx.wear.watchface.RenderParameters.Companion Companion;
diff --git a/wear/watchface/watchface/api/restricted_current.txt b/wear/watchface/watchface/api/restricted_current.txt
index 27d88a9..f66baa7 100644
--- a/wear/watchface/watchface/api/restricted_current.txt
+++ b/wear/watchface/watchface/api/restricted_current.txt
@@ -145,9 +145,11 @@
     method public androidx.wear.watchface.RenderParameters.HighlightLayer? getHighlightLayer();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> getLastComplicationTapDownEvents();
     method public java.util.Set<androidx.wear.watchface.style.WatchFaceLayer> getWatchFaceLayers();
+    method public boolean isForScreenshot();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.wear.watchface.data.RenderParametersWireFormat toWireFormat();
     property public final androidx.wear.watchface.DrawMode drawMode;
     property public final androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer;
+    property public final boolean isForScreenshot;
     property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents;
     property public final java.util.Set<androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers;
     field public static final androidx.wear.watchface.RenderParameters.Companion Companion;
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/RenderParameters.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/RenderParameters.kt
index 7a6d98f..1253b47 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/RenderParameters.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/RenderParameters.kt
@@ -97,6 +97,13 @@
     public val highlightLayer: HighlightLayer? = null,
     public val lastComplicationTapDownEvents: Map<Int, TapEvent> = emptyMap()
 ) {
+    /**
+     * Whether or not we're rendering for a screenshot. Some watch faces with animations may need to
+     * make adjustments when a screen shot is taken to achieve the best result.
+     */
+    public var isForScreenshot: Boolean = false
+      internal set
+
     init {
         require(watchFaceLayers.isNotEmpty() || highlightLayer != null) {
             "Either watchFaceLayers must be non empty or " +
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
index 52d3757..b8876f7 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/Renderer.kt
@@ -511,7 +511,9 @@
             )
             val prevRenderParameters = this.renderParameters
             this.renderParameters = renderParameters
+            this.renderParameters.isForScreenshot = true
             renderAndComposite(Canvas(bitmap), zonedDateTime)
+            this.renderParameters.isForScreenshot = false
             this.renderParameters = prevRenderParameters
             return bitmap
         }
@@ -1136,7 +1138,9 @@
                 runUiThreadGlCommands {
                     val prevRenderParameters = this@GlesRenderer.renderParameters
                     this@GlesRenderer.renderParameters = renderParameters
+                    renderParameters.isForScreenshot = true
                     renderAndComposite(zonedDateTime)
+                    renderParameters.isForScreenshot = false
                     this@GlesRenderer.renderParameters = prevRenderParameters
                     GLES20.glFinish()
                     GLES20.glReadPixels(
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
index f121c38..5f1088f 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
@@ -241,7 +241,7 @@
     interactiveFrameRateMs
 ) {
     public var lastOnDrawZonedDateTime: ZonedDateTime? = null
-    public var lastRenderParameters: RenderParameters = RenderParameters.DEFAULT_INTERACTIVE
+    public var lastRenderWasForScreenshot: Boolean? = null
 
     override fun render(
         canvas: Canvas,
@@ -249,7 +249,7 @@
         zonedDateTime: ZonedDateTime
     ) {
         lastOnDrawZonedDateTime = zonedDateTime
-        lastRenderParameters = renderParameters
+        lastRenderWasForScreenshot = renderParameters.isForScreenshot
     }
 
     override fun renderHighlightLayer(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index 7b3ca23..a0a46a8 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -112,6 +112,8 @@
 import org.robolectric.shadows.ShadowPendingIntent
 import java.io.StringWriter
 import java.time.Instant
+import java.time.ZoneId
+import java.time.ZonedDateTime
 import java.util.ArrayDeque
 import java.util.PriorityQueue
 import kotlin.test.assertFailsWith
@@ -4379,6 +4381,36 @@
         assertThat(getLeftShortTextComplicationDataText()).isEqualTo("A")
     }
 
+    @Test
+    public fun renderParameters_isScreenshot() {
+        initWallpaperInteractiveWatchFaceInstance(
+            WatchFaceType.ANALOG,
+            emptyList(),
+            UserStyleSchema(emptyList()),
+            WallpaperInteractiveWatchFaceInstanceParams(
+                "interactiveInstanceId",
+                DeviceConfig(
+                    false,
+                    false,
+                    0,
+                    0
+                ),
+                WatchUiState(false, 0),
+                UserStyle(emptyMap()).toWireFormat(),
+                null
+            )
+        )
+
+        renderer.takeScreenshot(
+            ZonedDateTime.ofInstant(Instant.EPOCH, ZoneId.of("GMT")),
+            RenderParameters(DrawMode.INTERACTIVE, WatchFaceLayer.ALL_WATCH_FACE_LAYERS, null)
+        )
+        assertThat(renderer.lastRenderWasForScreenshot).isTrue()
+
+        renderer.renderInternal(ZonedDateTime.ofInstant(Instant.EPOCH, ZoneId.of("GMT")))
+        assertThat(renderer.lastRenderWasForScreenshot).isFalse()
+    }
+
     private fun getLeftShortTextComplicationDataText(): CharSequence {
         val complication = complicationSlotsManager[
             LEFT_COMPLICATION_ID
diff --git a/webkit/webkit/src/androidTest/AndroidManifest.xml b/webkit/webkit/src/androidTest/AndroidManifest.xml
index 1d57ce4..df5f531 100644
--- a/webkit/webkit/src/androidTest/AndroidManifest.xml
+++ b/webkit/webkit/src/androidTest/AndroidManifest.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="androidx.webkit">
+          package="androidx.webkit.test">
     <!-- Note: we must provide INTERNET permission for
          ServiceWorkerWebSettingsCompatTest#testBlockNetworkLoads -->
     <uses-permission android:name="android.permission.INTERNET" />
@@ -26,6 +26,6 @@
                  android:hardwareAccelerated="true"
                  android:usesCleartextTraffic="true">
 
-        <activity android:name=".WebViewTestActivity"/>
+        <activity android:name="androidx.webkit.WebViewTestActivity"/>
     </application>
 </manifest>