Merge "Expand nullness lint check to more type-use positions" into androidx-main
diff --git a/activity/activity-compose/api/1.10.0-beta01.txt b/activity/activity-compose/api/1.10.0-beta01.txt
deleted file mode 100644
index df9fcaf..0000000
--- a/activity/activity-compose/api/1.10.0-beta01.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-// Signature format: 4.0
-package androidx.activity.compose {
-
-  public final class ActivityResultRegistryKt {
-    method @androidx.compose.runtime.Composable public static <I, O> androidx.activity.compose.ManagedActivityResultLauncher<I,O> rememberLauncherForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, kotlin.jvm.functions.Function1<? super O,kotlin.Unit> onResult);
-  }
-
-  public final class BackHandlerKt {
-    method @androidx.compose.runtime.Composable public static void BackHandler(optional boolean enabled, kotlin.jvm.functions.Function0<kotlin.Unit> onBack);
-  }
-
-  public final class ComponentActivityKt {
-    method public static void setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.CompositionContext? parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-  }
-
-  public final class LocalActivityResultRegistryOwner {
-    method @androidx.compose.runtime.Composable public androidx.activity.result.ActivityResultRegistryOwner? getCurrent();
-    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.result.ActivityResultRegistryOwner?> provides(androidx.activity.result.ActivityResultRegistryOwner registryOwner);
-    property @androidx.compose.runtime.Composable public final androidx.activity.result.ActivityResultRegistryOwner? current;
-    field public static final androidx.activity.compose.LocalActivityResultRegistryOwner INSTANCE;
-  }
-
-  public final class LocalFullyDrawnReporterOwner {
-    method @androidx.compose.runtime.Composable public androidx.activity.FullyDrawnReporterOwner? getCurrent();
-    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.FullyDrawnReporterOwner?> provides(androidx.activity.FullyDrawnReporterOwner fullyDrawnReporterOwner);
-    property @androidx.compose.runtime.Composable public final androidx.activity.FullyDrawnReporterOwner? current;
-    field public static final androidx.activity.compose.LocalFullyDrawnReporterOwner INSTANCE;
-  }
-
-  public final class LocalOnBackPressedDispatcherOwner {
-    method @androidx.compose.runtime.Composable public androidx.activity.OnBackPressedDispatcherOwner? getCurrent();
-    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.OnBackPressedDispatcherOwner?> provides(androidx.activity.OnBackPressedDispatcherOwner dispatcherOwner);
-    property @androidx.compose.runtime.Composable public final androidx.activity.OnBackPressedDispatcherOwner? current;
-    field public static final androidx.activity.compose.LocalOnBackPressedDispatcherOwner INSTANCE;
-  }
-
-  public final class ManagedActivityResultLauncher<I, O> extends androidx.activity.result.ActivityResultLauncher<I> {
-    method public androidx.activity.result.contract.ActivityResultContract<I,O> getContract();
-    method public void launch(I input, androidx.core.app.ActivityOptionsCompat? options);
-    method @Deprecated public void unregister();
-    property public androidx.activity.result.contract.ActivityResultContract<I,O> contract;
-  }
-
-  public final class PredictiveBackHandlerKt {
-    method @androidx.compose.runtime.Composable public static void PredictiveBackHandler(optional boolean enabled, kotlin.jvm.functions.Function2<kotlinx.coroutines.flow.Flow<androidx.activity.BackEventCompat>,? super kotlin.coroutines.Continuation<kotlin.Unit>,? extends java.lang.Object?> onBack);
-  }
-
-  public final class ReportDrawnKt {
-    method @androidx.compose.runtime.Composable public static void ReportDrawn();
-    method @androidx.compose.runtime.Composable public static void ReportDrawnAfter(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block);
-    method @androidx.compose.runtime.Composable public static void ReportDrawnWhen(kotlin.jvm.functions.Function0<java.lang.Boolean> predicate);
-  }
-
-}
-
diff --git a/activity/activity-compose/api/restricted_1.10.0-beta01.txt b/activity/activity-compose/api/restricted_1.10.0-beta01.txt
deleted file mode 100644
index df9fcaf..0000000
--- a/activity/activity-compose/api/restricted_1.10.0-beta01.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-// Signature format: 4.0
-package androidx.activity.compose {
-
-  public final class ActivityResultRegistryKt {
-    method @androidx.compose.runtime.Composable public static <I, O> androidx.activity.compose.ManagedActivityResultLauncher<I,O> rememberLauncherForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, kotlin.jvm.functions.Function1<? super O,kotlin.Unit> onResult);
-  }
-
-  public final class BackHandlerKt {
-    method @androidx.compose.runtime.Composable public static void BackHandler(optional boolean enabled, kotlin.jvm.functions.Function0<kotlin.Unit> onBack);
-  }
-
-  public final class ComponentActivityKt {
-    method public static void setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.CompositionContext? parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-  }
-
-  public final class LocalActivityResultRegistryOwner {
-    method @androidx.compose.runtime.Composable public androidx.activity.result.ActivityResultRegistryOwner? getCurrent();
-    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.result.ActivityResultRegistryOwner?> provides(androidx.activity.result.ActivityResultRegistryOwner registryOwner);
-    property @androidx.compose.runtime.Composable public final androidx.activity.result.ActivityResultRegistryOwner? current;
-    field public static final androidx.activity.compose.LocalActivityResultRegistryOwner INSTANCE;
-  }
-
-  public final class LocalFullyDrawnReporterOwner {
-    method @androidx.compose.runtime.Composable public androidx.activity.FullyDrawnReporterOwner? getCurrent();
-    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.FullyDrawnReporterOwner?> provides(androidx.activity.FullyDrawnReporterOwner fullyDrawnReporterOwner);
-    property @androidx.compose.runtime.Composable public final androidx.activity.FullyDrawnReporterOwner? current;
-    field public static final androidx.activity.compose.LocalFullyDrawnReporterOwner INSTANCE;
-  }
-
-  public final class LocalOnBackPressedDispatcherOwner {
-    method @androidx.compose.runtime.Composable public androidx.activity.OnBackPressedDispatcherOwner? getCurrent();
-    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.OnBackPressedDispatcherOwner?> provides(androidx.activity.OnBackPressedDispatcherOwner dispatcherOwner);
-    property @androidx.compose.runtime.Composable public final androidx.activity.OnBackPressedDispatcherOwner? current;
-    field public static final androidx.activity.compose.LocalOnBackPressedDispatcherOwner INSTANCE;
-  }
-
-  public final class ManagedActivityResultLauncher<I, O> extends androidx.activity.result.ActivityResultLauncher<I> {
-    method public androidx.activity.result.contract.ActivityResultContract<I,O> getContract();
-    method public void launch(I input, androidx.core.app.ActivityOptionsCompat? options);
-    method @Deprecated public void unregister();
-    property public androidx.activity.result.contract.ActivityResultContract<I,O> contract;
-  }
-
-  public final class PredictiveBackHandlerKt {
-    method @androidx.compose.runtime.Composable public static void PredictiveBackHandler(optional boolean enabled, kotlin.jvm.functions.Function2<kotlinx.coroutines.flow.Flow<androidx.activity.BackEventCompat>,? super kotlin.coroutines.Continuation<kotlin.Unit>,? extends java.lang.Object?> onBack);
-  }
-
-  public final class ReportDrawnKt {
-    method @androidx.compose.runtime.Composable public static void ReportDrawn();
-    method @androidx.compose.runtime.Composable public static void ReportDrawnAfter(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block);
-    method @androidx.compose.runtime.Composable public static void ReportDrawnWhen(kotlin.jvm.functions.Function0<java.lang.Boolean> predicate);
-  }
-
-}
-
diff --git a/activity/activity-ktx/api/1.10.0-beta01.txt b/activity/activity-ktx/api/1.10.0-beta01.txt
deleted file mode 100644
index e6f50d0..0000000
--- a/activity/activity-ktx/api/1.10.0-beta01.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 4.0
diff --git a/activity/activity-ktx/api/res-1.10.0-beta01.txt b/activity/activity-ktx/api/res-1.10.0-beta01.txt
deleted file mode 100644
index e69de29..0000000
--- a/activity/activity-ktx/api/res-1.10.0-beta01.txt
+++ /dev/null
diff --git a/activity/activity-ktx/api/restricted_1.10.0-beta01.txt b/activity/activity-ktx/api/restricted_1.10.0-beta01.txt
deleted file mode 100644
index e6f50d0..0000000
--- a/activity/activity-ktx/api/restricted_1.10.0-beta01.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 4.0
diff --git a/activity/activity/api/1.10.0-beta01.txt b/activity/activity/api/1.10.0-beta01.txt
deleted file mode 100644
index be7377b..0000000
--- a/activity/activity/api/1.10.0-beta01.txt
+++ /dev/null
@@ -1,548 +0,0 @@
-// Signature format: 4.0
-package androidx.activity {
-
-  public final class ActivityViewModelLazyKt {
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-  }
-
-  public final class BackEventCompat {
-    ctor @RequiresApi(34) public BackEventCompat(android.window.BackEvent backEvent);
-    ctor @VisibleForTesting public BackEventCompat(float touchX, float touchY, @FloatRange(from=0.0, to=1.0) float progress, int swipeEdge);
-    method public float getProgress();
-    method public int getSwipeEdge();
-    method public float getTouchX();
-    method public float getTouchY();
-    method @RequiresApi(34) public android.window.BackEvent toBackEvent();
-    property public final float progress;
-    property public final int swipeEdge;
-    property public final float touchX;
-    property public final float touchY;
-    field public static final androidx.activity.BackEventCompat.Companion Companion;
-    field public static final int EDGE_LEFT = 0; // 0x0
-    field public static final int EDGE_RIGHT = 1; // 0x1
-  }
-
-  public static final class BackEventCompat.Companion {
-  }
-
-  public class ComponentActivity extends android.app.Activity implements androidx.activity.result.ActivityResultCaller androidx.activity.result.ActivityResultRegistryOwner androidx.activity.contextaware.ContextAware androidx.activity.FullyDrawnReporterOwner androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.core.view.MenuHost androidx.activity.OnBackPressedDispatcherOwner androidx.core.content.OnConfigurationChangedProvider androidx.core.app.OnMultiWindowModeChangedProvider androidx.core.app.OnNewIntentProvider androidx.core.app.OnPictureInPictureModeChangedProvider androidx.core.content.OnTrimMemoryProvider androidx.core.app.OnUserLeaveHintProvider androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
-    ctor public ComponentActivity();
-    ctor @ContentView public ComponentActivity(@LayoutRes int contentLayoutId);
-    method public void addMenuProvider(androidx.core.view.MenuProvider provider);
-    method public void addMenuProvider(androidx.core.view.MenuProvider provider, androidx.lifecycle.LifecycleOwner owner);
-    method public void addMenuProvider(androidx.core.view.MenuProvider provider, androidx.lifecycle.LifecycleOwner owner, androidx.lifecycle.Lifecycle.State state);
-    method public final void addOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration> listener);
-    method public final void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-    method public final void addOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo> listener);
-    method public final void addOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent> listener);
-    method public final void addOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo> listener);
-    method public final void addOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer> listener);
-    method public final void addOnUserLeaveHintListener(Runnable listener);
-    method public final androidx.activity.result.ActivityResultRegistry getActivityResultRegistry();
-    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
-    method public androidx.activity.FullyDrawnReporter getFullyDrawnReporter();
-    method @Deprecated public Object? getLastCustomNonConfigurationInstance();
-    method public androidx.lifecycle.Lifecycle getLifecycle();
-    method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
-    method public final androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
-    method public androidx.lifecycle.ViewModelStore getViewModelStore();
-    method @CallSuper public void initializeViewTreeOwners();
-    method public void invalidateMenu();
-    method @Deprecated @CallSuper protected void onActivityResult(int requestCode, int resultCode, android.content.Intent? data);
-    method @Deprecated @CallSuper public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
-    method @Deprecated public Object? onRetainCustomNonConfigurationInstance();
-    method public final Object? onRetainNonConfigurationInstance();
-    method public android.content.Context? peekAvailableContext();
-    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
-    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultRegistry registry, androidx.activity.result.ActivityResultCallback<O> callback);
-    method public void removeMenuProvider(androidx.core.view.MenuProvider provider);
-    method public final void removeOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration> listener);
-    method public final void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-    method public final void removeOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo> listener);
-    method public final void removeOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent> listener);
-    method public final void removeOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo> listener);
-    method public final void removeOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer> listener);
-    method public final void removeOnUserLeaveHintListener(Runnable listener);
-    method @Deprecated public void startActivityForResult(android.content.Intent intent, int requestCode);
-    method @Deprecated public void startActivityForResult(android.content.Intent intent, int requestCode, android.os.Bundle? options);
-    method @Deprecated @kotlin.jvm.Throws(exceptionClasses=SendIntentException::class) public void startIntentSenderForResult(android.content.IntentSender intent, int requestCode, android.content.Intent? fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws android.content.IntentSender.SendIntentException;
-    method @Deprecated @kotlin.jvm.Throws(exceptionClasses=SendIntentException::class) public void startIntentSenderForResult(android.content.IntentSender intent, int requestCode, android.content.Intent? fillInIntent, int flagsMask, int flagsValues, int extraFlags, android.os.Bundle? options) throws android.content.IntentSender.SendIntentException;
-    property public final androidx.activity.result.ActivityResultRegistry activityResultRegistry;
-    property @CallSuper public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
-    property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
-    property public androidx.activity.FullyDrawnReporter fullyDrawnReporter;
-    property @Deprecated public Object? lastCustomNonConfigurationInstance;
-    property public androidx.lifecycle.Lifecycle lifecycle;
-    property public final androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
-    property public final androidx.savedstate.SavedStateRegistry savedStateRegistry;
-    property public androidx.lifecycle.ViewModelStore viewModelStore;
-  }
-
-  public class ComponentDialog extends android.app.Dialog implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner {
-    ctor public ComponentDialog(android.content.Context context);
-    ctor public ComponentDialog(android.content.Context context, optional @StyleRes int themeResId);
-    method public androidx.lifecycle.Lifecycle getLifecycle();
-    method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
-    method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
-    method @CallSuper public void initializeViewTreeOwners();
-    method @CallSuper public void onBackPressed();
-    property public androidx.lifecycle.Lifecycle lifecycle;
-    property public final androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
-    property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
-  }
-
-  public final class EdgeToEdge {
-    method public static void enable(androidx.activity.ComponentActivity);
-    method public static void enable(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle);
-    method public static void enable(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle, optional androidx.activity.SystemBarStyle navigationBarStyle);
-  }
-
-  public final class FullyDrawnReporter {
-    ctor public FullyDrawnReporter(java.util.concurrent.Executor executor, kotlin.jvm.functions.Function0<kotlin.Unit> reportFullyDrawn);
-    method public void addOnReportDrawnListener(kotlin.jvm.functions.Function0<kotlin.Unit> callback);
-    method public void addReporter();
-    method public boolean isFullyDrawnReported();
-    method public void removeOnReportDrawnListener(kotlin.jvm.functions.Function0<kotlin.Unit> callback);
-    method public void removeReporter();
-    property public final boolean isFullyDrawnReported;
-  }
-
-  public final class FullyDrawnReporterKt {
-    method public static suspend inline Object? reportWhenComplete(androidx.activity.FullyDrawnReporter, kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> reporter, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-  }
-
-  public interface FullyDrawnReporterOwner {
-    method public androidx.activity.FullyDrawnReporter getFullyDrawnReporter();
-    property public abstract androidx.activity.FullyDrawnReporter fullyDrawnReporter;
-  }
-
-  public abstract class OnBackPressedCallback {
-    ctor public OnBackPressedCallback(boolean enabled);
-    method @MainThread public void handleOnBackCancelled();
-    method @MainThread public abstract void handleOnBackPressed();
-    method @MainThread public void handleOnBackProgressed(androidx.activity.BackEventCompat backEvent);
-    method @MainThread public void handleOnBackStarted(androidx.activity.BackEventCompat backEvent);
-    method @MainThread public final boolean isEnabled();
-    method @MainThread public final void remove();
-    method @MainThread public final void setEnabled(boolean);
-    property @MainThread public final boolean isEnabled;
-  }
-
-  public final class OnBackPressedDispatcher {
-    ctor public OnBackPressedDispatcher();
-    ctor public OnBackPressedDispatcher(optional Runnable? fallbackOnBackPressed);
-    ctor public OnBackPressedDispatcher(Runnable? fallbackOnBackPressed, androidx.core.util.Consumer<java.lang.Boolean>? onHasEnabledCallbacksChanged);
-    method @MainThread public void addCallback(androidx.activity.OnBackPressedCallback onBackPressedCallback);
-    method @MainThread public void addCallback(androidx.lifecycle.LifecycleOwner owner, androidx.activity.OnBackPressedCallback onBackPressedCallback);
-    method @MainThread @VisibleForTesting public void dispatchOnBackCancelled();
-    method @MainThread @VisibleForTesting public void dispatchOnBackProgressed(androidx.activity.BackEventCompat backEvent);
-    method @MainThread @VisibleForTesting public void dispatchOnBackStarted(androidx.activity.BackEventCompat backEvent);
-    method @MainThread public boolean hasEnabledCallbacks();
-    method @MainThread public void onBackPressed();
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public void setOnBackInvokedDispatcher(android.window.OnBackInvokedDispatcher invoker);
-  }
-
-  public final class OnBackPressedDispatcherKt {
-    method public static androidx.activity.OnBackPressedCallback addCallback(androidx.activity.OnBackPressedDispatcher, optional androidx.lifecycle.LifecycleOwner? owner, optional boolean enabled, kotlin.jvm.functions.Function1<? super androidx.activity.OnBackPressedCallback,kotlin.Unit> onBackPressed);
-  }
-
-  public interface OnBackPressedDispatcherOwner extends androidx.lifecycle.LifecycleOwner {
-    method public androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
-    property public abstract androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
-  }
-
-  public final class PipHintTrackerKt {
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static suspend Object? trackPipAnimationHintView(android.app.Activity, android.view.View view, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-  }
-
-  public final class SystemBarStyle {
-    method public static androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim);
-    method public static androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim, optional kotlin.jvm.functions.Function1<? super android.content.res.Resources,java.lang.Boolean> detectDarkMode);
-    method public static androidx.activity.SystemBarStyle dark(@ColorInt int scrim);
-    method public static androidx.activity.SystemBarStyle light(@ColorInt int scrim, @ColorInt int darkScrim);
-    field public static final androidx.activity.SystemBarStyle.Companion Companion;
-  }
-
-  public static final class SystemBarStyle.Companion {
-    method public androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim);
-    method public androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim, optional kotlin.jvm.functions.Function1<? super android.content.res.Resources,java.lang.Boolean> detectDarkMode);
-    method public androidx.activity.SystemBarStyle dark(@ColorInt int scrim);
-    method public androidx.activity.SystemBarStyle light(@ColorInt int scrim, @ColorInt int darkScrim);
-  }
-
-  public final class ViewTreeFullyDrawnReporterOwner {
-    method public static androidx.activity.FullyDrawnReporterOwner? get(android.view.View);
-    method public static void set(android.view.View, androidx.activity.FullyDrawnReporterOwner fullyDrawnReporterOwner);
-  }
-
-  public final class ViewTreeOnBackPressedDispatcherOwner {
-    method public static androidx.activity.OnBackPressedDispatcherOwner? get(android.view.View);
-    method public static void set(android.view.View, androidx.activity.OnBackPressedDispatcherOwner onBackPressedDispatcherOwner);
-  }
-
-}
-
-package androidx.activity.contextaware {
-
-  public interface ContextAware {
-    method public void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-    method public android.content.Context? peekAvailableContext();
-    method public void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-  }
-
-  public final class ContextAwareHelper {
-    ctor public ContextAwareHelper();
-    method public void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-    method public void clearAvailableContext();
-    method public void dispatchOnContextAvailable(android.content.Context context);
-    method public android.content.Context? peekAvailableContext();
-    method public void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-  }
-
-  public final class ContextAwareKt {
-    method public static suspend inline <R> Object? withContextAvailable(androidx.activity.contextaware.ContextAware, kotlin.jvm.functions.Function1<android.content.Context,R> onContextAvailable, kotlin.coroutines.Continuation<R>);
-  }
-
-  public fun interface OnContextAvailableListener {
-    method public void onContextAvailable(android.content.Context context);
-  }
-
-}
-
-package androidx.activity.result {
-
-  public final class ActivityResult implements android.os.Parcelable {
-    ctor public ActivityResult(int resultCode, android.content.Intent? data);
-    method public int describeContents();
-    method public android.content.Intent? getData();
-    method public int getResultCode();
-    method public static String resultCodeToString(int resultCode);
-    method public void writeToParcel(android.os.Parcel dest, int flags);
-    property public final android.content.Intent? data;
-    property public final int resultCode;
-    field public static final android.os.Parcelable.Creator<androidx.activity.result.ActivityResult> CREATOR;
-    field public static final androidx.activity.result.ActivityResult.Companion Companion;
-  }
-
-  public static final class ActivityResult.Companion {
-    method public String resultCodeToString(int resultCode);
-  }
-
-  public fun interface ActivityResultCallback<O> {
-    method public void onActivityResult(O result);
-  }
-
-  public interface ActivityResultCaller {
-    method public <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
-    method public <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultRegistry registry, androidx.activity.result.ActivityResultCallback<O> callback);
-  }
-
-  public final class ActivityResultCallerKt {
-    method public static <I, O> androidx.activity.result.ActivityResultLauncher<kotlin.Unit> registerForActivityResult(androidx.activity.result.ActivityResultCaller, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, androidx.activity.result.ActivityResultRegistry registry, kotlin.jvm.functions.Function1<O,kotlin.Unit> callback);
-    method public static <I, O> androidx.activity.result.ActivityResultLauncher<kotlin.Unit> registerForActivityResult(androidx.activity.result.ActivityResultCaller, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, kotlin.jvm.functions.Function1<O,kotlin.Unit> callback);
-  }
-
-  public final class ActivityResultKt {
-    method public static operator int component1(androidx.activity.result.ActivityResult);
-    method public static operator android.content.Intent? component2(androidx.activity.result.ActivityResult);
-  }
-
-  public abstract class ActivityResultLauncher<I> {
-    ctor public ActivityResultLauncher();
-    method public abstract androidx.activity.result.contract.ActivityResultContract<I,? extends java.lang.Object?> getContract();
-    method public void launch(I input);
-    method public abstract void launch(I input, androidx.core.app.ActivityOptionsCompat? options);
-    method @MainThread public abstract void unregister();
-    property public abstract androidx.activity.result.contract.ActivityResultContract<I,? extends java.lang.Object?> contract;
-  }
-
-  public final class ActivityResultLauncherKt {
-    method public static void launch(androidx.activity.result.ActivityResultLauncher<java.lang.Void?>, optional androidx.core.app.ActivityOptionsCompat? options);
-    method public static void launchUnit(androidx.activity.result.ActivityResultLauncher<kotlin.Unit>, optional androidx.core.app.ActivityOptionsCompat? options);
-  }
-
-  public abstract class ActivityResultRegistry {
-    ctor public ActivityResultRegistry();
-    method @MainThread public final boolean dispatchResult(int requestCode, int resultCode, android.content.Intent? data);
-    method @MainThread public final <O> boolean dispatchResult(int requestCode, O result);
-    method @MainThread public abstract <I, O> void onLaunch(int requestCode, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, androidx.core.app.ActivityOptionsCompat? options);
-    method public final void onRestoreInstanceState(android.os.Bundle? savedInstanceState);
-    method public final void onSaveInstanceState(android.os.Bundle outState);
-    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> register(String key, androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
-    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> register(String key, androidx.lifecycle.LifecycleOwner lifecycleOwner, androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
-  }
-
-  public interface ActivityResultRegistryOwner {
-    method public androidx.activity.result.ActivityResultRegistry getActivityResultRegistry();
-    property public abstract androidx.activity.result.ActivityResultRegistry activityResultRegistry;
-  }
-
-  public final class IntentSenderRequest implements android.os.Parcelable {
-    method public int describeContents();
-    method public android.content.Intent? getFillInIntent();
-    method public int getFlagsMask();
-    method public int getFlagsValues();
-    method public android.content.IntentSender getIntentSender();
-    method public void writeToParcel(android.os.Parcel dest, int flags);
-    property public final android.content.Intent? fillInIntent;
-    property public final int flagsMask;
-    property public final int flagsValues;
-    property public final android.content.IntentSender intentSender;
-    field public static final android.os.Parcelable.Creator<androidx.activity.result.IntentSenderRequest> CREATOR;
-    field public static final androidx.activity.result.IntentSenderRequest.Companion Companion;
-  }
-
-  public static final class IntentSenderRequest.Builder {
-    ctor public IntentSenderRequest.Builder(android.app.PendingIntent pendingIntent);
-    ctor public IntentSenderRequest.Builder(android.content.IntentSender intentSender);
-    method public androidx.activity.result.IntentSenderRequest build();
-    method public androidx.activity.result.IntentSenderRequest.Builder setFillInIntent(android.content.Intent? fillInIntent);
-    method public androidx.activity.result.IntentSenderRequest.Builder setFlags(int values, int mask);
-  }
-
-  public static final class IntentSenderRequest.Companion {
-  }
-
-  public final class PickVisualMediaRequest {
-    method public long getAccentColor();
-    method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab getDefaultTab();
-    method public int getMaxItems();
-    method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType getMediaType();
-    method public boolean isCustomAccentColorApplied();
-    method public boolean isOrderedSelection();
-    property public final long accentColor;
-    property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab;
-    property public final boolean isCustomAccentColorApplied;
-    property public final boolean isOrderedSelection;
-    property public final int maxItems;
-    property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType;
-  }
-
-  public static final class PickVisualMediaRequest.Builder {
-    ctor public PickVisualMediaRequest.Builder();
-    method public androidx.activity.result.PickVisualMediaRequest build();
-    method public androidx.activity.result.PickVisualMediaRequest.Builder setAccentColor(long accentColor);
-    method public androidx.activity.result.PickVisualMediaRequest.Builder setDefaultTab(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
-    method public androidx.activity.result.PickVisualMediaRequest.Builder setMaxItems(@IntRange(from=2L) int maxItems);
-    method public androidx.activity.result.PickVisualMediaRequest.Builder setMediaType(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
-    method public androidx.activity.result.PickVisualMediaRequest.Builder setOrderedSelection(boolean isOrderedSelection);
-  }
-
-  public final class PickVisualMediaRequestKt {
-    method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
-    method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems);
-    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
-    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(long accentColor, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
-  }
-
-}
-
-package androidx.activity.result.contract {
-
-  public abstract class ActivityResultContract<I, O> {
-    ctor public ActivityResultContract();
-    method public abstract android.content.Intent createIntent(android.content.Context context, I input);
-    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<O>? getSynchronousResult(android.content.Context context, I input);
-    method public abstract O parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static final class ActivityResultContract.SynchronousResult<T> {
-    ctor public ActivityResultContract.SynchronousResult(T value);
-    method public T getValue();
-    property public final T value;
-  }
-
-  public final class ActivityResultContracts {
-  }
-
-  public static class ActivityResultContracts.CaptureVideo extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,java.lang.Boolean> {
-    ctor public ActivityResultContracts.CaptureVideo();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, android.net.Uri input);
-    method public final Boolean parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.CreateDocument extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,android.net.Uri?> {
-    ctor @Deprecated public ActivityResultContracts.CreateDocument();
-    ctor public ActivityResultContracts.CreateDocument(String mimeType);
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String input);
-    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.GetContent extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,android.net.Uri?> {
-    ctor public ActivityResultContracts.GetContent();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String input);
-    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.GetMultipleContents extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,java.util.List<android.net.Uri>> {
-    ctor public ActivityResultContracts.GetMultipleContents();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, String input);
-    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.OpenDocument extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],android.net.Uri?> {
-    ctor public ActivityResultContracts.OpenDocument();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String[] input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String[] input);
-    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  @RequiresApi(21) public static class ActivityResultContracts.OpenDocumentTree extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri?,android.net.Uri?> {
-    ctor public ActivityResultContracts.OpenDocumentTree();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri? input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, android.net.Uri? input);
-    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.OpenMultipleDocuments extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],java.util.List<android.net.Uri>> {
-    ctor public ActivityResultContracts.OpenMultipleDocuments();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String[] input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, String[] input);
-    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static final class ActivityResultContracts.PickContact extends androidx.activity.result.contract.ActivityResultContract<java.lang.Void?,android.net.Uri?> {
-    ctor public ActivityResultContracts.PickContact();
-    method public android.content.Intent createIntent(android.content.Context context, Void? input);
-    method public android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.PickMultipleVisualMedia extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.PickVisualMediaRequest,java.util.List<android.net.Uri>> {
-    ctor public ActivityResultContracts.PickMultipleVisualMedia();
-    ctor public ActivityResultContracts.PickMultipleVisualMedia(optional int maxItems);
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
-    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.PickVisualMedia extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.PickVisualMediaRequest,android.net.Uri?> {
-    ctor public ActivityResultContracts.PickVisualMedia();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
-    method @Deprecated public static final boolean isPhotoPickerAvailable();
-    method public static final boolean isPhotoPickerAvailable(android.content.Context context);
-    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-    field public static final String ACTION_SYSTEM_FALLBACK_PICK_IMAGES = "androidx.activity.result.contract.action.PICK_IMAGES";
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion Companion;
-    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_ACCENT_COLOR = "androidx.activity.result.contract.extra.PICK_IMAGES_ACCENT_COLOR";
-    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_IN_ORDER = "androidx.activity.result.contract.extra.PICK_IMAGES_IN_ORDER";
-    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_LAUNCH_TAB = "androidx.activity.result.contract.extra.PICK_IMAGES_LAUNCH_TAB";
-    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_MAX = "androidx.activity.result.contract.extra.PICK_IMAGES_MAX";
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.Companion {
-    method @Deprecated public boolean isPhotoPickerAvailable();
-    method public boolean isPhotoPickerAvailable(android.content.Context context);
-  }
-
-  public abstract static class ActivityResultContracts.PickVisualMedia.DefaultTab {
-    method public abstract int getValue();
-    property public abstract int value;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
-    method public int getValue();
-    property public int value;
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab INSTANCE;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
-    method public int getValue();
-    property public int value;
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab INSTANCE;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.ImageAndVideo implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageAndVideo INSTANCE;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.ImageOnly implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageOnly INSTANCE;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.SingleMimeType implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
-    ctor public ActivityResultContracts.PickVisualMedia.SingleMimeType(String mimeType);
-    method public String getMimeType();
-    property public final String mimeType;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.VideoOnly implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VideoOnly INSTANCE;
-  }
-
-  public static sealed interface ActivityResultContracts.PickVisualMedia.VisualMediaType {
-  }
-
-  public static final class ActivityResultContracts.RequestMultiplePermissions extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],java.util.Map<java.lang.String,java.lang.Boolean>> {
-    ctor public ActivityResultContracts.RequestMultiplePermissions();
-    method public android.content.Intent createIntent(android.content.Context context, String[] input);
-    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.Map<java.lang.String,java.lang.Boolean>>? getSynchronousResult(android.content.Context context, String[] input);
-    method public java.util.Map<java.lang.String,java.lang.Boolean> parseResult(int resultCode, android.content.Intent? intent);
-    field public static final String ACTION_REQUEST_PERMISSIONS = "androidx.activity.result.contract.action.REQUEST_PERMISSIONS";
-    field public static final androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion Companion;
-    field public static final String EXTRA_PERMISSIONS = "androidx.activity.result.contract.extra.PERMISSIONS";
-    field public static final String EXTRA_PERMISSION_GRANT_RESULTS = "androidx.activity.result.contract.extra.PERMISSION_GRANT_RESULTS";
-  }
-
-  public static final class ActivityResultContracts.RequestMultiplePermissions.Companion {
-  }
-
-  public static final class ActivityResultContracts.RequestPermission extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,java.lang.Boolean> {
-    ctor public ActivityResultContracts.RequestPermission();
-    method public android.content.Intent createIntent(android.content.Context context, String input);
-    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, String input);
-    method public Boolean parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static final class ActivityResultContracts.StartActivityForResult extends androidx.activity.result.contract.ActivityResultContract<android.content.Intent,androidx.activity.result.ActivityResult> {
-    ctor public ActivityResultContracts.StartActivityForResult();
-    method public android.content.Intent createIntent(android.content.Context context, android.content.Intent input);
-    method public androidx.activity.result.ActivityResult parseResult(int resultCode, android.content.Intent? intent);
-    field public static final androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult.Companion Companion;
-    field public static final String EXTRA_ACTIVITY_OPTIONS_BUNDLE = "androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE";
-  }
-
-  public static final class ActivityResultContracts.StartActivityForResult.Companion {
-  }
-
-  public static final class ActivityResultContracts.StartIntentSenderForResult extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.IntentSenderRequest,androidx.activity.result.ActivityResult> {
-    ctor public ActivityResultContracts.StartIntentSenderForResult();
-    method public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.IntentSenderRequest input);
-    method public androidx.activity.result.ActivityResult parseResult(int resultCode, android.content.Intent? intent);
-    field public static final String ACTION_INTENT_SENDER_REQUEST = "androidx.activity.result.contract.action.INTENT_SENDER_REQUEST";
-    field public static final androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult.Companion Companion;
-    field public static final String EXTRA_INTENT_SENDER_REQUEST = "androidx.activity.result.contract.extra.INTENT_SENDER_REQUEST";
-    field public static final String EXTRA_SEND_INTENT_EXCEPTION = "androidx.activity.result.contract.extra.SEND_INTENT_EXCEPTION";
-  }
-
-  public static final class ActivityResultContracts.StartIntentSenderForResult.Companion {
-  }
-
-  public static class ActivityResultContracts.TakePicture extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,java.lang.Boolean> {
-    ctor public ActivityResultContracts.TakePicture();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, android.net.Uri input);
-    method public final Boolean parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.TakePicturePreview extends androidx.activity.result.contract.ActivityResultContract<java.lang.Void?,android.graphics.Bitmap?> {
-    ctor public ActivityResultContracts.TakePicturePreview();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, Void? input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.graphics.Bitmap?>? getSynchronousResult(android.content.Context context, Void? input);
-    method public final android.graphics.Bitmap? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  @Deprecated public static class ActivityResultContracts.TakeVideo extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,android.graphics.Bitmap?> {
-    ctor @Deprecated public ActivityResultContracts.TakeVideo();
-    method @Deprecated @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
-    method @Deprecated public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.graphics.Bitmap?>? getSynchronousResult(android.content.Context context, android.net.Uri input);
-    method @Deprecated public final android.graphics.Bitmap? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-}
-
diff --git a/activity/activity/api/res-1.10.0-beta01.txt b/activity/activity/api/res-1.10.0-beta01.txt
deleted file mode 100644
index e69de29..0000000
--- a/activity/activity/api/res-1.10.0-beta01.txt
+++ /dev/null
diff --git a/activity/activity/api/restricted_1.10.0-beta01.txt b/activity/activity/api/restricted_1.10.0-beta01.txt
deleted file mode 100644
index 3fc0729..0000000
--- a/activity/activity/api/restricted_1.10.0-beta01.txt
+++ /dev/null
@@ -1,547 +0,0 @@
-// Signature format: 4.0
-package androidx.activity {
-
-  public final class ActivityViewModelLazyKt {
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-  }
-
-  public final class BackEventCompat {
-    ctor @RequiresApi(34) public BackEventCompat(android.window.BackEvent backEvent);
-    ctor @VisibleForTesting public BackEventCompat(float touchX, float touchY, @FloatRange(from=0.0, to=1.0) float progress, int swipeEdge);
-    method public float getProgress();
-    method public int getSwipeEdge();
-    method public float getTouchX();
-    method public float getTouchY();
-    method @RequiresApi(34) public android.window.BackEvent toBackEvent();
-    property public final float progress;
-    property public final int swipeEdge;
-    property public final float touchX;
-    property public final float touchY;
-    field public static final androidx.activity.BackEventCompat.Companion Companion;
-    field public static final int EDGE_LEFT = 0; // 0x0
-    field public static final int EDGE_RIGHT = 1; // 0x1
-  }
-
-  public static final class BackEventCompat.Companion {
-  }
-
-  public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.activity.result.ActivityResultCaller androidx.activity.result.ActivityResultRegistryOwner androidx.activity.contextaware.ContextAware androidx.activity.FullyDrawnReporterOwner androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.core.view.MenuHost androidx.activity.OnBackPressedDispatcherOwner androidx.core.content.OnConfigurationChangedProvider androidx.core.app.OnMultiWindowModeChangedProvider androidx.core.app.OnNewIntentProvider androidx.core.app.OnPictureInPictureModeChangedProvider androidx.core.content.OnTrimMemoryProvider androidx.core.app.OnUserLeaveHintProvider androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
-    ctor public ComponentActivity();
-    ctor @ContentView public ComponentActivity(@LayoutRes int contentLayoutId);
-    method public void addMenuProvider(androidx.core.view.MenuProvider provider);
-    method public void addMenuProvider(androidx.core.view.MenuProvider provider, androidx.lifecycle.LifecycleOwner owner);
-    method public void addMenuProvider(androidx.core.view.MenuProvider provider, androidx.lifecycle.LifecycleOwner owner, androidx.lifecycle.Lifecycle.State state);
-    method public final void addOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration> listener);
-    method public final void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-    method public final void addOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo> listener);
-    method public final void addOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent> listener);
-    method public final void addOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo> listener);
-    method public final void addOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer> listener);
-    method public final void addOnUserLeaveHintListener(Runnable listener);
-    method public final androidx.activity.result.ActivityResultRegistry getActivityResultRegistry();
-    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
-    method public androidx.activity.FullyDrawnReporter getFullyDrawnReporter();
-    method @Deprecated public Object? getLastCustomNonConfigurationInstance();
-    method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
-    method public final androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
-    method public androidx.lifecycle.ViewModelStore getViewModelStore();
-    method @CallSuper public void initializeViewTreeOwners();
-    method public void invalidateMenu();
-    method @Deprecated @CallSuper protected void onActivityResult(int requestCode, int resultCode, android.content.Intent? data);
-    method @Deprecated @CallSuper public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
-    method @Deprecated public Object? onRetainCustomNonConfigurationInstance();
-    method public final Object? onRetainNonConfigurationInstance();
-    method public android.content.Context? peekAvailableContext();
-    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
-    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultRegistry registry, androidx.activity.result.ActivityResultCallback<O> callback);
-    method public void removeMenuProvider(androidx.core.view.MenuProvider provider);
-    method public final void removeOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration> listener);
-    method public final void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-    method public final void removeOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo> listener);
-    method public final void removeOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent> listener);
-    method public final void removeOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo> listener);
-    method public final void removeOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer> listener);
-    method public final void removeOnUserLeaveHintListener(Runnable listener);
-    method @Deprecated public void startActivityForResult(android.content.Intent intent, int requestCode);
-    method @Deprecated public void startActivityForResult(android.content.Intent intent, int requestCode, android.os.Bundle? options);
-    method @Deprecated @kotlin.jvm.Throws(exceptionClasses=SendIntentException::class) public void startIntentSenderForResult(android.content.IntentSender intent, int requestCode, android.content.Intent? fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws android.content.IntentSender.SendIntentException;
-    method @Deprecated @kotlin.jvm.Throws(exceptionClasses=SendIntentException::class) public void startIntentSenderForResult(android.content.IntentSender intent, int requestCode, android.content.Intent? fillInIntent, int flagsMask, int flagsValues, int extraFlags, android.os.Bundle? options) throws android.content.IntentSender.SendIntentException;
-    property public final androidx.activity.result.ActivityResultRegistry activityResultRegistry;
-    property @CallSuper public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
-    property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
-    property public androidx.activity.FullyDrawnReporter fullyDrawnReporter;
-    property @Deprecated public Object? lastCustomNonConfigurationInstance;
-    property public androidx.lifecycle.Lifecycle lifecycle;
-    property public final androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
-    property public final androidx.savedstate.SavedStateRegistry savedStateRegistry;
-    property public androidx.lifecycle.ViewModelStore viewModelStore;
-  }
-
-  public class ComponentDialog extends android.app.Dialog implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner {
-    ctor public ComponentDialog(android.content.Context context);
-    ctor public ComponentDialog(android.content.Context context, optional @StyleRes int themeResId);
-    method public androidx.lifecycle.Lifecycle getLifecycle();
-    method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
-    method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
-    method @CallSuper public void initializeViewTreeOwners();
-    method @CallSuper public void onBackPressed();
-    property public androidx.lifecycle.Lifecycle lifecycle;
-    property public final androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
-    property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
-  }
-
-  public final class EdgeToEdge {
-    method public static void enable(androidx.activity.ComponentActivity);
-    method public static void enable(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle);
-    method public static void enable(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle, optional androidx.activity.SystemBarStyle navigationBarStyle);
-  }
-
-  public final class FullyDrawnReporter {
-    ctor public FullyDrawnReporter(java.util.concurrent.Executor executor, kotlin.jvm.functions.Function0<kotlin.Unit> reportFullyDrawn);
-    method public void addOnReportDrawnListener(kotlin.jvm.functions.Function0<kotlin.Unit> callback);
-    method public void addReporter();
-    method public boolean isFullyDrawnReported();
-    method public void removeOnReportDrawnListener(kotlin.jvm.functions.Function0<kotlin.Unit> callback);
-    method public void removeReporter();
-    property public final boolean isFullyDrawnReported;
-  }
-
-  public final class FullyDrawnReporterKt {
-    method public static suspend inline Object? reportWhenComplete(androidx.activity.FullyDrawnReporter, kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> reporter, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-  }
-
-  public interface FullyDrawnReporterOwner {
-    method public androidx.activity.FullyDrawnReporter getFullyDrawnReporter();
-    property public abstract androidx.activity.FullyDrawnReporter fullyDrawnReporter;
-  }
-
-  public abstract class OnBackPressedCallback {
-    ctor public OnBackPressedCallback(boolean enabled);
-    method @MainThread public void handleOnBackCancelled();
-    method @MainThread public abstract void handleOnBackPressed();
-    method @MainThread public void handleOnBackProgressed(androidx.activity.BackEventCompat backEvent);
-    method @MainThread public void handleOnBackStarted(androidx.activity.BackEventCompat backEvent);
-    method @MainThread public final boolean isEnabled();
-    method @MainThread public final void remove();
-    method @MainThread public final void setEnabled(boolean);
-    property @MainThread public final boolean isEnabled;
-  }
-
-  public final class OnBackPressedDispatcher {
-    ctor public OnBackPressedDispatcher();
-    ctor public OnBackPressedDispatcher(optional Runnable? fallbackOnBackPressed);
-    ctor public OnBackPressedDispatcher(Runnable? fallbackOnBackPressed, androidx.core.util.Consumer<java.lang.Boolean>? onHasEnabledCallbacksChanged);
-    method @MainThread public void addCallback(androidx.activity.OnBackPressedCallback onBackPressedCallback);
-    method @MainThread public void addCallback(androidx.lifecycle.LifecycleOwner owner, androidx.activity.OnBackPressedCallback onBackPressedCallback);
-    method @MainThread @VisibleForTesting public void dispatchOnBackCancelled();
-    method @MainThread @VisibleForTesting public void dispatchOnBackProgressed(androidx.activity.BackEventCompat backEvent);
-    method @MainThread @VisibleForTesting public void dispatchOnBackStarted(androidx.activity.BackEventCompat backEvent);
-    method @MainThread public boolean hasEnabledCallbacks();
-    method @MainThread public void onBackPressed();
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public void setOnBackInvokedDispatcher(android.window.OnBackInvokedDispatcher invoker);
-  }
-
-  public final class OnBackPressedDispatcherKt {
-    method public static androidx.activity.OnBackPressedCallback addCallback(androidx.activity.OnBackPressedDispatcher, optional androidx.lifecycle.LifecycleOwner? owner, optional boolean enabled, kotlin.jvm.functions.Function1<? super androidx.activity.OnBackPressedCallback,kotlin.Unit> onBackPressed);
-  }
-
-  public interface OnBackPressedDispatcherOwner extends androidx.lifecycle.LifecycleOwner {
-    method public androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
-    property public abstract androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
-  }
-
-  public final class PipHintTrackerKt {
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static suspend Object? trackPipAnimationHintView(android.app.Activity, android.view.View view, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-  }
-
-  public final class SystemBarStyle {
-    method public static androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim);
-    method public static androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim, optional kotlin.jvm.functions.Function1<? super android.content.res.Resources,java.lang.Boolean> detectDarkMode);
-    method public static androidx.activity.SystemBarStyle dark(@ColorInt int scrim);
-    method public static androidx.activity.SystemBarStyle light(@ColorInt int scrim, @ColorInt int darkScrim);
-    field public static final androidx.activity.SystemBarStyle.Companion Companion;
-  }
-
-  public static final class SystemBarStyle.Companion {
-    method public androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim);
-    method public androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim, optional kotlin.jvm.functions.Function1<? super android.content.res.Resources,java.lang.Boolean> detectDarkMode);
-    method public androidx.activity.SystemBarStyle dark(@ColorInt int scrim);
-    method public androidx.activity.SystemBarStyle light(@ColorInt int scrim, @ColorInt int darkScrim);
-  }
-
-  public final class ViewTreeFullyDrawnReporterOwner {
-    method public static androidx.activity.FullyDrawnReporterOwner? get(android.view.View);
-    method public static void set(android.view.View, androidx.activity.FullyDrawnReporterOwner fullyDrawnReporterOwner);
-  }
-
-  public final class ViewTreeOnBackPressedDispatcherOwner {
-    method public static androidx.activity.OnBackPressedDispatcherOwner? get(android.view.View);
-    method public static void set(android.view.View, androidx.activity.OnBackPressedDispatcherOwner onBackPressedDispatcherOwner);
-  }
-
-}
-
-package androidx.activity.contextaware {
-
-  public interface ContextAware {
-    method public void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-    method public android.content.Context? peekAvailableContext();
-    method public void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-  }
-
-  public final class ContextAwareHelper {
-    ctor public ContextAwareHelper();
-    method public void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-    method public void clearAvailableContext();
-    method public void dispatchOnContextAvailable(android.content.Context context);
-    method public android.content.Context? peekAvailableContext();
-    method public void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
-  }
-
-  public final class ContextAwareKt {
-    method public static suspend inline <R> Object? withContextAvailable(androidx.activity.contextaware.ContextAware, kotlin.jvm.functions.Function1<android.content.Context,R> onContextAvailable, kotlin.coroutines.Continuation<R>);
-  }
-
-  public fun interface OnContextAvailableListener {
-    method public void onContextAvailable(android.content.Context context);
-  }
-
-}
-
-package androidx.activity.result {
-
-  public final class ActivityResult implements android.os.Parcelable {
-    ctor public ActivityResult(int resultCode, android.content.Intent? data);
-    method public int describeContents();
-    method public android.content.Intent? getData();
-    method public int getResultCode();
-    method public static String resultCodeToString(int resultCode);
-    method public void writeToParcel(android.os.Parcel dest, int flags);
-    property public final android.content.Intent? data;
-    property public final int resultCode;
-    field public static final android.os.Parcelable.Creator<androidx.activity.result.ActivityResult> CREATOR;
-    field public static final androidx.activity.result.ActivityResult.Companion Companion;
-  }
-
-  public static final class ActivityResult.Companion {
-    method public String resultCodeToString(int resultCode);
-  }
-
-  public fun interface ActivityResultCallback<O> {
-    method public void onActivityResult(O result);
-  }
-
-  public interface ActivityResultCaller {
-    method public <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
-    method public <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultRegistry registry, androidx.activity.result.ActivityResultCallback<O> callback);
-  }
-
-  public final class ActivityResultCallerKt {
-    method public static <I, O> androidx.activity.result.ActivityResultLauncher<kotlin.Unit> registerForActivityResult(androidx.activity.result.ActivityResultCaller, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, androidx.activity.result.ActivityResultRegistry registry, kotlin.jvm.functions.Function1<O,kotlin.Unit> callback);
-    method public static <I, O> androidx.activity.result.ActivityResultLauncher<kotlin.Unit> registerForActivityResult(androidx.activity.result.ActivityResultCaller, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, kotlin.jvm.functions.Function1<O,kotlin.Unit> callback);
-  }
-
-  public final class ActivityResultKt {
-    method public static operator int component1(androidx.activity.result.ActivityResult);
-    method public static operator android.content.Intent? component2(androidx.activity.result.ActivityResult);
-  }
-
-  public abstract class ActivityResultLauncher<I> {
-    ctor public ActivityResultLauncher();
-    method public abstract androidx.activity.result.contract.ActivityResultContract<I,? extends java.lang.Object?> getContract();
-    method public void launch(I input);
-    method public abstract void launch(I input, androidx.core.app.ActivityOptionsCompat? options);
-    method @MainThread public abstract void unregister();
-    property public abstract androidx.activity.result.contract.ActivityResultContract<I,? extends java.lang.Object?> contract;
-  }
-
-  public final class ActivityResultLauncherKt {
-    method public static void launch(androidx.activity.result.ActivityResultLauncher<java.lang.Void?>, optional androidx.core.app.ActivityOptionsCompat? options);
-    method public static void launchUnit(androidx.activity.result.ActivityResultLauncher<kotlin.Unit>, optional androidx.core.app.ActivityOptionsCompat? options);
-  }
-
-  public abstract class ActivityResultRegistry {
-    ctor public ActivityResultRegistry();
-    method @MainThread public final boolean dispatchResult(int requestCode, int resultCode, android.content.Intent? data);
-    method @MainThread public final <O> boolean dispatchResult(int requestCode, O result);
-    method @MainThread public abstract <I, O> void onLaunch(int requestCode, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, androidx.core.app.ActivityOptionsCompat? options);
-    method public final void onRestoreInstanceState(android.os.Bundle? savedInstanceState);
-    method public final void onSaveInstanceState(android.os.Bundle outState);
-    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> register(String key, androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
-    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> register(String key, androidx.lifecycle.LifecycleOwner lifecycleOwner, androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
-  }
-
-  public interface ActivityResultRegistryOwner {
-    method public androidx.activity.result.ActivityResultRegistry getActivityResultRegistry();
-    property public abstract androidx.activity.result.ActivityResultRegistry activityResultRegistry;
-  }
-
-  public final class IntentSenderRequest implements android.os.Parcelable {
-    method public int describeContents();
-    method public android.content.Intent? getFillInIntent();
-    method public int getFlagsMask();
-    method public int getFlagsValues();
-    method public android.content.IntentSender getIntentSender();
-    method public void writeToParcel(android.os.Parcel dest, int flags);
-    property public final android.content.Intent? fillInIntent;
-    property public final int flagsMask;
-    property public final int flagsValues;
-    property public final android.content.IntentSender intentSender;
-    field public static final android.os.Parcelable.Creator<androidx.activity.result.IntentSenderRequest> CREATOR;
-    field public static final androidx.activity.result.IntentSenderRequest.Companion Companion;
-  }
-
-  public static final class IntentSenderRequest.Builder {
-    ctor public IntentSenderRequest.Builder(android.app.PendingIntent pendingIntent);
-    ctor public IntentSenderRequest.Builder(android.content.IntentSender intentSender);
-    method public androidx.activity.result.IntentSenderRequest build();
-    method public androidx.activity.result.IntentSenderRequest.Builder setFillInIntent(android.content.Intent? fillInIntent);
-    method public androidx.activity.result.IntentSenderRequest.Builder setFlags(int values, int mask);
-  }
-
-  public static final class IntentSenderRequest.Companion {
-  }
-
-  public final class PickVisualMediaRequest {
-    method public long getAccentColor();
-    method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab getDefaultTab();
-    method public int getMaxItems();
-    method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType getMediaType();
-    method public boolean isCustomAccentColorApplied();
-    method public boolean isOrderedSelection();
-    property public final long accentColor;
-    property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab;
-    property public final boolean isCustomAccentColorApplied;
-    property public final boolean isOrderedSelection;
-    property public final int maxItems;
-    property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType;
-  }
-
-  public static final class PickVisualMediaRequest.Builder {
-    ctor public PickVisualMediaRequest.Builder();
-    method public androidx.activity.result.PickVisualMediaRequest build();
-    method public androidx.activity.result.PickVisualMediaRequest.Builder setAccentColor(long accentColor);
-    method public androidx.activity.result.PickVisualMediaRequest.Builder setDefaultTab(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
-    method public androidx.activity.result.PickVisualMediaRequest.Builder setMaxItems(@IntRange(from=2L) int maxItems);
-    method public androidx.activity.result.PickVisualMediaRequest.Builder setMediaType(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
-    method public androidx.activity.result.PickVisualMediaRequest.Builder setOrderedSelection(boolean isOrderedSelection);
-  }
-
-  public final class PickVisualMediaRequestKt {
-    method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
-    method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems);
-    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
-    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(long accentColor, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
-  }
-
-}
-
-package androidx.activity.result.contract {
-
-  public abstract class ActivityResultContract<I, O> {
-    ctor public ActivityResultContract();
-    method public abstract android.content.Intent createIntent(android.content.Context context, I input);
-    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<O>? getSynchronousResult(android.content.Context context, I input);
-    method public abstract O parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static final class ActivityResultContract.SynchronousResult<T> {
-    ctor public ActivityResultContract.SynchronousResult(T value);
-    method public T getValue();
-    property public final T value;
-  }
-
-  public final class ActivityResultContracts {
-  }
-
-  public static class ActivityResultContracts.CaptureVideo extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,java.lang.Boolean> {
-    ctor public ActivityResultContracts.CaptureVideo();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, android.net.Uri input);
-    method public final Boolean parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.CreateDocument extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,android.net.Uri?> {
-    ctor @Deprecated public ActivityResultContracts.CreateDocument();
-    ctor public ActivityResultContracts.CreateDocument(String mimeType);
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String input);
-    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.GetContent extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,android.net.Uri?> {
-    ctor public ActivityResultContracts.GetContent();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String input);
-    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.GetMultipleContents extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,java.util.List<android.net.Uri>> {
-    ctor public ActivityResultContracts.GetMultipleContents();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, String input);
-    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.OpenDocument extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],android.net.Uri?> {
-    ctor public ActivityResultContracts.OpenDocument();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String[] input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String[] input);
-    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  @RequiresApi(21) public static class ActivityResultContracts.OpenDocumentTree extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri?,android.net.Uri?> {
-    ctor public ActivityResultContracts.OpenDocumentTree();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri? input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, android.net.Uri? input);
-    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.OpenMultipleDocuments extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],java.util.List<android.net.Uri>> {
-    ctor public ActivityResultContracts.OpenMultipleDocuments();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String[] input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, String[] input);
-    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static final class ActivityResultContracts.PickContact extends androidx.activity.result.contract.ActivityResultContract<java.lang.Void?,android.net.Uri?> {
-    ctor public ActivityResultContracts.PickContact();
-    method public android.content.Intent createIntent(android.content.Context context, Void? input);
-    method public android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.PickMultipleVisualMedia extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.PickVisualMediaRequest,java.util.List<android.net.Uri>> {
-    ctor public ActivityResultContracts.PickMultipleVisualMedia();
-    ctor public ActivityResultContracts.PickMultipleVisualMedia(optional int maxItems);
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
-    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.PickVisualMedia extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.PickVisualMediaRequest,android.net.Uri?> {
-    ctor public ActivityResultContracts.PickVisualMedia();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
-    method @Deprecated public static final boolean isPhotoPickerAvailable();
-    method public static final boolean isPhotoPickerAvailable(android.content.Context context);
-    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
-    field public static final String ACTION_SYSTEM_FALLBACK_PICK_IMAGES = "androidx.activity.result.contract.action.PICK_IMAGES";
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion Companion;
-    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_ACCENT_COLOR = "androidx.activity.result.contract.extra.PICK_IMAGES_ACCENT_COLOR";
-    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_IN_ORDER = "androidx.activity.result.contract.extra.PICK_IMAGES_IN_ORDER";
-    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_LAUNCH_TAB = "androidx.activity.result.contract.extra.PICK_IMAGES_LAUNCH_TAB";
-    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_MAX = "androidx.activity.result.contract.extra.PICK_IMAGES_MAX";
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.Companion {
-    method @Deprecated public boolean isPhotoPickerAvailable();
-    method public boolean isPhotoPickerAvailable(android.content.Context context);
-  }
-
-  public abstract static class ActivityResultContracts.PickVisualMedia.DefaultTab {
-    method public abstract int getValue();
-    property public abstract int value;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
-    method public int getValue();
-    property public int value;
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab INSTANCE;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
-    method public int getValue();
-    property public int value;
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab INSTANCE;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.ImageAndVideo implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageAndVideo INSTANCE;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.ImageOnly implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageOnly INSTANCE;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.SingleMimeType implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
-    ctor public ActivityResultContracts.PickVisualMedia.SingleMimeType(String mimeType);
-    method public String getMimeType();
-    property public final String mimeType;
-  }
-
-  public static final class ActivityResultContracts.PickVisualMedia.VideoOnly implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
-    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VideoOnly INSTANCE;
-  }
-
-  public static sealed interface ActivityResultContracts.PickVisualMedia.VisualMediaType {
-  }
-
-  public static final class ActivityResultContracts.RequestMultiplePermissions extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],java.util.Map<java.lang.String,java.lang.Boolean>> {
-    ctor public ActivityResultContracts.RequestMultiplePermissions();
-    method public android.content.Intent createIntent(android.content.Context context, String[] input);
-    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.Map<java.lang.String,java.lang.Boolean>>? getSynchronousResult(android.content.Context context, String[] input);
-    method public java.util.Map<java.lang.String,java.lang.Boolean> parseResult(int resultCode, android.content.Intent? intent);
-    field public static final String ACTION_REQUEST_PERMISSIONS = "androidx.activity.result.contract.action.REQUEST_PERMISSIONS";
-    field public static final androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion Companion;
-    field public static final String EXTRA_PERMISSIONS = "androidx.activity.result.contract.extra.PERMISSIONS";
-    field public static final String EXTRA_PERMISSION_GRANT_RESULTS = "androidx.activity.result.contract.extra.PERMISSION_GRANT_RESULTS";
-  }
-
-  public static final class ActivityResultContracts.RequestMultiplePermissions.Companion {
-  }
-
-  public static final class ActivityResultContracts.RequestPermission extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,java.lang.Boolean> {
-    ctor public ActivityResultContracts.RequestPermission();
-    method public android.content.Intent createIntent(android.content.Context context, String input);
-    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, String input);
-    method public Boolean parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static final class ActivityResultContracts.StartActivityForResult extends androidx.activity.result.contract.ActivityResultContract<android.content.Intent,androidx.activity.result.ActivityResult> {
-    ctor public ActivityResultContracts.StartActivityForResult();
-    method public android.content.Intent createIntent(android.content.Context context, android.content.Intent input);
-    method public androidx.activity.result.ActivityResult parseResult(int resultCode, android.content.Intent? intent);
-    field public static final androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult.Companion Companion;
-    field public static final String EXTRA_ACTIVITY_OPTIONS_BUNDLE = "androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE";
-  }
-
-  public static final class ActivityResultContracts.StartActivityForResult.Companion {
-  }
-
-  public static final class ActivityResultContracts.StartIntentSenderForResult extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.IntentSenderRequest,androidx.activity.result.ActivityResult> {
-    ctor public ActivityResultContracts.StartIntentSenderForResult();
-    method public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.IntentSenderRequest input);
-    method public androidx.activity.result.ActivityResult parseResult(int resultCode, android.content.Intent? intent);
-    field public static final String ACTION_INTENT_SENDER_REQUEST = "androidx.activity.result.contract.action.INTENT_SENDER_REQUEST";
-    field public static final androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult.Companion Companion;
-    field public static final String EXTRA_INTENT_SENDER_REQUEST = "androidx.activity.result.contract.extra.INTENT_SENDER_REQUEST";
-    field public static final String EXTRA_SEND_INTENT_EXCEPTION = "androidx.activity.result.contract.extra.SEND_INTENT_EXCEPTION";
-  }
-
-  public static final class ActivityResultContracts.StartIntentSenderForResult.Companion {
-  }
-
-  public static class ActivityResultContracts.TakePicture extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,java.lang.Boolean> {
-    ctor public ActivityResultContracts.TakePicture();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, android.net.Uri input);
-    method public final Boolean parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  public static class ActivityResultContracts.TakePicturePreview extends androidx.activity.result.contract.ActivityResultContract<java.lang.Void?,android.graphics.Bitmap?> {
-    ctor public ActivityResultContracts.TakePicturePreview();
-    method @CallSuper public android.content.Intent createIntent(android.content.Context context, Void? input);
-    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.graphics.Bitmap?>? getSynchronousResult(android.content.Context context, Void? input);
-    method public final android.graphics.Bitmap? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-  @Deprecated public static class ActivityResultContracts.TakeVideo extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,android.graphics.Bitmap?> {
-    ctor @Deprecated public ActivityResultContracts.TakeVideo();
-    method @Deprecated @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
-    method @Deprecated public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.graphics.Bitmap?>? getSynchronousResult(android.content.Context context, android.net.Uri input);
-    method @Deprecated public final android.graphics.Bitmap? parseResult(int resultCode, android.content.Intent? intent);
-  }
-
-}
-
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/AssertsAssert.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/AssertsAssert.kt
new file mode 100644
index 0000000..81d31ba
--- /dev/null
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/AssertsAssert.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 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.benchmark
+
+import kotlin.test.Test
+import org.junit.Assert
+
+class AssertsAssert {
+    // Makes sure that the test APKs are debuggable and asserts are thrown.
+    @Test
+    fun checkAssertsAreEnforced() {
+        Assert.assertThrows(AssertionError::class.java) { assert(false) }
+    }
+}
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/InMemoryTracingTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/InMemoryTracingTest.kt
index 2a471b2..d1dbc7a 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/InMemoryTracingTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/InMemoryTracingTest.kt
@@ -22,6 +22,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import java.io.File
 import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Before
@@ -81,7 +82,7 @@
 
         // verify events
         trace.packet[1].apply {
-            assert(timestamp in beforeTime..afterTime)
+            assertTrue(timestamp in beforeTime..afterTime)
             assertEquals(
                 TracePacket(
                     timestamp = timestamp,
@@ -99,7 +100,7 @@
             )
         }
         trace.packet[2].apply {
-            assert(timestamp in beforeTime..afterTime)
+            assertTrue(timestamp in beforeTime..afterTime)
             assertEquals(
                 TracePacket(
                     timestamp = timestamp,
@@ -115,6 +116,92 @@
             )
         }
     }
+
+    @Test
+    fun traceWithCounters() {
+        val beforeTime = 100L
+        val afterTime = 200L
+
+        // test counter embedded in beginSection
+        InMemoryTracing.beginSection(
+            "test trace section",
+            beforeTime,
+            counterNames = listOf("counterLabel"),
+            counterValues = listOf(0.1)
+        )
+        InMemoryTracing.endSection(afterTime)
+
+        // test counter on its own
+        InMemoryTracing.counter("counterLabel", 1.0, afterTime)
+
+        val trace = InMemoryTracing.commitToTrace("testLabel")
+
+        assertEquals(5, trace.packet.size)
+
+        // verify first track, for slices
+        val sliceDescriptor = trace.packet.first().track_descriptor
+        assertNotNull(sliceDescriptor)
+        assertEquals("testLabel", sliceDescriptor.name)
+        // verify second track, for counters
+        val counterDescriptor = trace.packet[1].track_descriptor
+        assertNotNull(counterDescriptor)
+        assertEquals("counterLabel", counterDescriptor.name)
+
+        // verify events
+        trace.packet[2].apply {
+            assertEquals(timestamp, beforeTime)
+            assertEquals(
+                TracePacket(
+                    timestamp = timestamp,
+                    timestamp_clock_id = 3,
+                    trusted_packet_sequence_id = trusted_packet_sequence_id,
+                    track_event =
+                        TrackEvent(
+                            type = TrackEvent.Type.TYPE_SLICE_BEGIN,
+                            track_uuid = sliceDescriptor.uuid,
+                            categories = listOf("benchmark"),
+                            name = "test trace section",
+                            extra_double_counter_track_uuids = listOf(counterDescriptor.uuid!!),
+                            extra_double_counter_values = listOf(0.1)
+                        )
+                ),
+                this
+            )
+        }
+        trace.packet[3].apply {
+            assertEquals(timestamp, afterTime)
+            assertEquals(
+                TracePacket(
+                    timestamp = timestamp,
+                    timestamp_clock_id = 3,
+                    trusted_packet_sequence_id = trusted_packet_sequence_id,
+                    track_event =
+                        TrackEvent(
+                            type = TrackEvent.Type.TYPE_SLICE_END,
+                            track_uuid = sliceDescriptor.uuid,
+                        )
+                ),
+                this
+            )
+        }
+        trace.packet[4].apply {
+            assertEquals(timestamp, afterTime)
+            assertEquals(
+                TracePacket(
+                    timestamp = timestamp,
+                    timestamp_clock_id = 3,
+                    trusted_packet_sequence_id = trusted_packet_sequence_id,
+                    track_event =
+                        TrackEvent(
+                            type = TrackEvent.Type.TYPE_COUNTER,
+                            track_uuid = counterDescriptor.uuid,
+                            double_counter_value = 1.0,
+                        )
+                ),
+                this
+            )
+        }
+    }
 }
 
 @Suppress("SameParameterValue")
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
index e07f881..6068fa7b 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
@@ -52,9 +52,10 @@
             // noop
         }
         assertNotNull(perfettoTrace)
-        assert(perfettoTrace!!.path.matches(Regex(".*/testTrace_[0-9-]+.perfetto-trace"))) {
+        assertTrue(
+            perfettoTrace!!.path.matches(Regex(".*/testTrace_[0-9-]+.perfetto-trace")),
             "$perfettoTrace didn't match!"
-        }
+        )
     }
 
     private fun verifyRecordSuccess(config: PerfettoConfig) {
@@ -68,9 +69,10 @@
             // noop
         }
         assertNotNull(perfettoTrace)
-        assert(perfettoTrace!!.path.matches(Regex(".*/${label}_[0-9-]+.perfetto-trace"))) {
+        assertTrue(
+            perfettoTrace!!.path.matches(Regex(".*/${label}_[0-9-]+.perfetto-trace")),
             "$perfettoTrace didn't match!"
-        }
+        )
     }
 
     private fun verifyRecordFails(config: PerfettoConfig) {
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
index 3b38d13..9c359f2 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
@@ -76,7 +76,7 @@
         tempFile.writeText(fakeText)
 
         ResultWriter.writeReport(tempFile, listOf(reportA, reportB))
-        assert(!tempFile.readText().startsWith(fakeText))
+        assertTrue(!tempFile.readText().startsWith(fakeText))
     }
 
     @Test
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/InMemoryTracing.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/InMemoryTracing.kt
index a68a4e7..b3e2c64 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/InMemoryTracing.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/InMemoryTracing.kt
@@ -19,6 +19,7 @@
 import android.os.Process
 import androidx.annotation.RestrictTo
 import androidx.benchmark.InMemoryTracing.commitToTrace
+import perfetto.protos.CounterDescriptor
 import perfetto.protos.ThreadDescriptor
 import perfetto.protos.Trace
 import perfetto.protos.TracePacket
@@ -65,18 +66,41 @@
     private val TRACK_EVENT_CATEGORIES = listOf("benchmark")
 
     /**
-     * For perf/simplicity, this isn't protected by a lock - it should only every be accessed by the
+     * For perf/simplicity, this isn't protected by a lock - it should only ever be accessed by the
      * test thread, and dumped/reset between tests.
      */
     val events = mutableListOf<TracePacket>()
 
+    /** Map of counter name to UUID, populated by [counterNameToTrackUuid] */
+    private val counterTracks = mutableMapOf<String, Long>()
+
+    private fun counterNameToTrackUuid(name: String): Long {
+        return counterTracks.getOrPut(name) { UUID + 1 + counterTracks.size }
+    }
+
     fun clearEvents() {
         events.clear()
+        counterTracks.clear()
     }
 
     /** Capture trace state, and return as a Trace(), which can be appended to a trace file. */
     fun commitToTrace(label: String): Trace {
         val capturedEvents = events.toList()
+        val capturedCounterDescriptors =
+            counterTracks.map { (name, uuid) ->
+                TracePacket(
+                    timestamp_clock_id = CLOCK_ID,
+                    incremental_state_cleared = true,
+                    track_descriptor =
+                        TrackDescriptor(
+                            uuid = uuid,
+                            parent_uuid = UUID,
+                            name = name,
+                            counter = CounterDescriptor()
+                        )
+                )
+            }
+
         clearEvents()
         return Trace(
             listOf(
@@ -94,11 +118,17 @@
                             disallow_merging_with_system_tracks = true
                         )
                 )
-            ) + capturedEvents
+            ) + capturedCounterDescriptors + capturedEvents
         )
     }
 
-    fun beginSection(label: String, nanoTime: Long = System.nanoTime()) {
+    fun beginSection(
+        label: String,
+        nanoTime: Long = System.nanoTime(),
+        counterNames: List<String> = emptyList(),
+        counterValues: List<Double> = emptyList()
+    ) {
+        require(counterNames.size == counterValues.size)
         events.add(
             TracePacket(
                 timestamp = nanoTime,
@@ -109,7 +139,10 @@
                         type = TrackEvent.Type.TYPE_SLICE_BEGIN,
                         track_uuid = UUID,
                         categories = TRACK_EVENT_CATEGORIES,
-                        name = label
+                        name = label,
+                        extra_double_counter_values = counterValues,
+                        extra_double_counter_track_uuids =
+                            counterNames.map { counterNameToTrackUuid(it) },
                     )
             )
         )
@@ -129,6 +162,23 @@
             )
         )
     }
+
+    fun counter(name: String, value: Double, nanoTime: Long = System.nanoTime()) {
+        events.add(
+            TracePacket(
+                timestamp = nanoTime,
+                timestamp_clock_id = CLOCK_ID,
+                trusted_packet_sequence_id = TRUSTED_PACKET_SEQUENCE_ID,
+                track_event =
+                    TrackEvent(
+                        type = TrackEvent.Type.TYPE_COUNTER,
+                        double_counter_value = value,
+                        track_uuid = counterNameToTrackUuid(name),
+                        // track_uuid = UUID
+                    )
+            )
+        )
+    }
 }
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricsContainer.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricsContainer.kt
index 710ef11..56a5ffe 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricsContainer.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricsContainer.kt
@@ -136,30 +136,42 @@
      * Call exactly once at the end of a benchmark.
      */
     fun captureFinished(maxIterations: Int): List<MetricResult> {
+        val results =
+            names.mapIndexed { index, name ->
+                val metricData =
+                    List(repeatCount) {
+                        // convert to floats and divide by iter count here for efficiency
+                        data[it][index] / maxIterations.toDouble()
+                    }
+                metricData.chunked(10).forEachIndexed { chunkNum, chunk ->
+                    Log.d(
+                        BenchmarkState.TAG,
+                        name +
+                            "[%2d:%2d]: %s"
+                                .format(
+                                    chunkNum * 10,
+                                    (chunkNum + 1) * 10,
+                                    chunk.joinToString(" ") { it.toLong().toString() }
+                                )
+                    )
+                }
+                MetricResult(name, metricData)
+            }
+
+        val metricTraceLabels = names.map { "metric: $it" }
         for (i in 0..repeatTiming.lastIndex step 2) {
-            InMemoryTracing.beginSection("measurement ${i / 2}", nanoTime = repeatTiming[i])
+            val measurementIndex = i / 2
+            InMemoryTracing.beginSection(
+                "measurement $measurementIndex",
+                nanoTime = repeatTiming[i],
+                counterNames = metricTraceLabels,
+                counterValues = results.map { it.data[measurementIndex] }
+            )
             InMemoryTracing.endSection(nanoTime = repeatTiming[i + 1])
         }
+        // to clarify when measurement ends, reset metrics to 0
+        metricTraceLabels.forEach { InMemoryTracing.counter(it, 0.0, repeatTiming.last()) }
 
-        return names.mapIndexed { index, name ->
-            val metricData =
-                List(repeatCount) {
-                    // convert to floats and divide by iter count here for efficiency
-                    data[it][index] / maxIterations.toDouble()
-                }
-            metricData.chunked(10).forEachIndexed { chunkNum, chunk ->
-                Log.d(
-                    BenchmarkState.TAG,
-                    name +
-                        "[%2d:%2d]: %s"
-                            .format(
-                                chunkNum * 10,
-                                (chunkNum + 1) * 10,
-                                chunk.joinToString(" ") { it.toLong().toString() }
-                            )
-                )
-            }
-            MetricResult(name, metricData)
-        }
+        return results
     }
 }
diff --git a/benchmark/benchmark-common/src/main/proto/perfetto_trace.proto b/benchmark/benchmark-common/src/main/proto/perfetto_trace.proto
index 12b204f..5372b59 100644
--- a/benchmark/benchmark-common/src/main/proto/perfetto_trace.proto
+++ b/benchmark/benchmark-common/src/main/proto/perfetto_trace.proto
@@ -94,9 +94,18 @@
   enum Type {
     TYPE_SLICE_BEGIN = 1;
     TYPE_SLICE_END = 2;
+    TYPE_COUNTER = 4;
   }
   optional Type type = 9;
   optional uint64 track_uuid = 11;
+  oneof counter_value_field {
+    int64 counter_value = 30;
+    double double_counter_value = 44;
+  }
+  repeated uint64 extra_counter_track_uuids = 31;
+  repeated int64 extra_counter_values = 12;
+  repeated uint64 extra_double_counter_track_uuids = 45;
+  repeated double extra_double_counter_values = 46;
 }
 
 message ThreadDescriptor {
@@ -104,10 +113,15 @@
   optional int32 tid = 2;
 }
 
+message CounterDescriptor {
+}
+
 message TrackDescriptor {
   optional uint64 uuid = 1;
+  optional uint64 parent_uuid = 5;
   optional string name = 2;
   optional ThreadDescriptor thread = 4;
+  optional CounterDescriptor counter = 8;
   optional bool disallow_merging_with_system_tracks = 9;
 }
 
diff --git a/benchmark/benchmark-macro/build.gradle b/benchmark/benchmark-macro/build.gradle
index 3e00f6b..23786dd 100644
--- a/benchmark/benchmark-macro/build.gradle
+++ b/benchmark/benchmark-macro/build.gradle
@@ -166,7 +166,7 @@
     def installTask = tasks.findByPath(
             ":benchmark:integration-tests:macrobenchmark-target:installRelease")
     if (installTask != null) {
-        tasks.getByPath(":benchmark:benchmark-macro:connectedDebugAndroidTest")
+        tasks.getByPath(":benchmark:benchmark-macro:connectedReleaseAndroidTest")
                 .dependsOn(installTask)
     }
 }
diff --git a/benchmark/benchmark/build.gradle b/benchmark/benchmark/build.gradle
index cb95e6d..3c10910 100644
--- a/benchmark/benchmark/build.gradle
+++ b/benchmark/benchmark/build.gradle
@@ -40,6 +40,7 @@
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.junit)
     androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.kotlinTest)
 }
 
 android {
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/BenchmarkConfigTest.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/BenchmarkConfigTest.kt
new file mode 100644
index 0000000..a350a82
--- /dev/null
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/BenchmarkConfigTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019 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.benchmark.benchmark
+
+import android.content.pm.ApplicationInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertNotEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * These tests validate build time properties of benchmarks.
+ *
+ * These tests are enforced in presubmit, even if dryRunMode=true is passed. Standard
+ * microbenchmarks will not perform these checks when dryRunMode=false.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class BenchmarkConfigTest {
+    private val arguments = InstrumentationRegistry.getArguments()
+    private val contextTest = InstrumentationRegistry.getInstrumentation().context
+    private val contextTarget = InstrumentationRegistry.getInstrumentation().targetContext
+
+    @Test
+    fun coverageDisabled() {
+        assertNotEquals(
+            illegal = "true",
+            actual = arguments.getString("coverage"),
+            message = "Coverage must not be enabled in microbench instrumentation args"
+        )
+    }
+
+    @Test
+    fun selfInstrumenting() {
+        assertEquals(
+            expected = contextTest.packageName,
+            actual = contextTarget.packageName,
+            message =
+                "Microbenchmark must be self-instrumenting," +
+                    " test pkg=${contextTest.packageName}," +
+                    " target pkg=${contextTarget.packageName}"
+        )
+    }
+
+    @Test
+    fun debuggableFalse() {
+        val debuggable = contextTest.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0
+        assertFalse(debuggable, "Microbenchmark must not be debuggable")
+    }
+}
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KLibDumpParser.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KLibDumpParser.kt
index 41335c3..be77a52 100644
--- a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KLibDumpParser.kt
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KLibDumpParser.kt
@@ -95,7 +95,6 @@
                                 platformTargets = listOf(),
                                 compilerVersion = "",
                                 abiVersion = "",
-                                libraryVersion = "",
                                 irProviderName = ""
                             )
                     )
diff --git a/biometric/biometric/src/main/java/androidx/biometric/PromptContentViewWithMoreOptionsButton.java b/biometric/biometric/src/main/java/androidx/biometric/PromptContentViewWithMoreOptionsButton.java
index eb4f9aa..76ce1a1 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/PromptContentViewWithMoreOptionsButton.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/PromptContentViewWithMoreOptionsButton.java
@@ -45,15 +45,12 @@
  *     .setContentView(
  *         new PromptContentViewWithMoreOptionsButton.Builder()
  *             .setDescription("test description")
- *             .setMoreOptionsButtonListener(executor, listener)
  *             .build()
  *      )
  *     .build();
  * </pre>
  */
 public final class PromptContentViewWithMoreOptionsButton implements PromptContentView {
-    static final int MAX_DESCRIPTION_CHARACTER_NUMBER = 225;
-
     private final String mDescription;
 
     private PromptContentViewWithMoreOptionsButton(@NonNull String description) {
@@ -89,10 +86,6 @@
         @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public Builder setDescription(@NonNull String description) {
-            if (description.length() > MAX_DESCRIPTION_CHARACTER_NUMBER) {
-                throw new IllegalArgumentException("The character number of description exceeds "
-                        + MAX_DESCRIPTION_CHARACTER_NUMBER);
-            }
             mDescription = description;
             return this;
         }
diff --git a/biometric/biometric/src/main/java/androidx/biometric/PromptVerticalListContentView.java b/biometric/biometric/src/main/java/androidx/biometric/PromptVerticalListContentView.java
index 58d7be2..b464d05 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/PromptVerticalListContentView.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/PromptVerticalListContentView.java
@@ -42,10 +42,6 @@
  * </pre>
  */
 public final class PromptVerticalListContentView implements PromptContentView {
-    static final int MAX_ITEM_NUMBER = 20;
-    static final int MAX_EACH_ITEM_CHARACTER_NUMBER = 640;
-    static final int MAX_DESCRIPTION_CHARACTER_NUMBER = 225;
-
     private final List<PromptContentItem> mContentList;
     private final String mDescription;
 
@@ -93,10 +89,6 @@
          */
         @NonNull
         public Builder setDescription(@NonNull String description) {
-            if (description.length() > MAX_DESCRIPTION_CHARACTER_NUMBER) {
-                throw new IllegalArgumentException("The character number of description exceeds "
-                        + MAX_DESCRIPTION_CHARACTER_NUMBER);
-            }
             mDescription = description;
             return this;
         }
@@ -112,7 +104,6 @@
         @NonNull
         public Builder addListItem(@NonNull PromptContentItem listItem) {
             mContentList.add(listItem);
-            checkItemLimits(listItem);
             return this;
         }
 
@@ -128,34 +119,9 @@
         @NonNull
         public Builder addListItem(@NonNull PromptContentItem listItem, int index) {
             mContentList.add(index, listItem);
-            checkItemLimits(listItem);
             return this;
         }
 
-        private void checkItemLimits(@NonNull PromptContentItem listItem) {
-            if (doesListItemExceedsCharLimit(listItem)) {
-                throw new IllegalArgumentException(
-                        "The character number of list item exceeds "
-                                + MAX_EACH_ITEM_CHARACTER_NUMBER);
-            }
-            if (mContentList.size() > MAX_ITEM_NUMBER) {
-                throw new IllegalArgumentException(
-                        "The number of list items exceeds " + MAX_ITEM_NUMBER);
-            }
-        }
-
-        private boolean doesListItemExceedsCharLimit(PromptContentItem listItem) {
-            if (listItem instanceof PromptContentItemPlainText) {
-                return ((PromptContentItemPlainText) listItem).getText().length()
-                        > MAX_EACH_ITEM_CHARACTER_NUMBER;
-            } else if (listItem instanceof PromptContentItemBulletedText) {
-                return ((PromptContentItemBulletedText) listItem).getText().length()
-                        > MAX_EACH_ITEM_CHARACTER_NUMBER;
-            } else {
-                return false;
-            }
-        }
-
 
         /**
          * Creates a {@link PromptVerticalListContentView}.
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
index 76cb02d..6f6e0b20 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
@@ -185,11 +185,7 @@
 
             compile.pluginClasspath.from(kotlinPluginProvider.get())
 
-            // todo(b/291587160): enable when Compose compiler 2.0.20 is merged
-            // compile.enableFeatureFlag(ComposeFeatureFlag.StrongSkipping)
-            // compile.enableFeatureFlag(ComposeFeatureFlag.OptimizeNonSkippingGroups)
-            compile.addPluginOption(ComposeCompileOptions.StrongSkipping, "true")
-            compile.addPluginOption(ComposeCompileOptions.NonSkippingGroupOptimization, "true")
+            compile.enableFeatureFlag(ComposeFeatureFlag.OptimizeNonSkippingGroups)
             if (shouldPublish) {
                 compile.addPluginOption(ComposeCompileOptions.SourceOption, "true")
             }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
index 101493e..e178e58 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
@@ -150,6 +150,9 @@
 
 const val FORCE_KOTLIN_2_0_TARGET = "androidx.forceKotlin20Target"
 
+/** Defined by AndroidX Benchmark Plugin, may be used for local experiments with compilation */
+const val FORCE_BENCHMARK_AOT_COMPILATION = "androidx.benchmark.forceaotcompilation"
+
 val ALL_ANDROIDX_PROPERTIES =
     setOf(
         ADD_GROUP_CONSTRAINTS,
@@ -186,6 +189,7 @@
         USE_JSPECIFY_ANNOTATIONS,
         YARN_OFFLINE_MODE,
         FORCE_KOTLIN_2_0_TARGET,
+        FORCE_BENCHMARK_AOT_COMPILATION,
     ) + AndroidConfigImpl.GRADLE_PROPERTIES
 
 fun Project.shouldForceKotlin20Target() =
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 9b239ec4..f875c2f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -48,6 +48,7 @@
 import com.android.build.api.dsl.KotlinMultiplatformAndroidTestOnJvmCompilation
 import com.android.build.api.dsl.LibraryExtension
 import com.android.build.api.dsl.PrivacySandboxSdkExtension
+import com.android.build.api.dsl.TestBuildType
 import com.android.build.api.dsl.TestExtension
 import com.android.build.api.variant.AndroidComponentsExtension
 import com.android.build.api.variant.ApplicationAndroidComponentsExtension
@@ -822,11 +823,11 @@
     }
 
     private fun configureWithLibraryPlugin(project: Project, androidXExtension: AndroidXExtension) {
+        val buildTypeForTests = "release"
         project.extensions.getByType<LibraryExtension>().apply {
             publishing { singleVariant(DEFAULT_PUBLISH_CONFIG) }
 
             configureAndroidBaseOptions(project, androidXExtension)
-
             val debugSigningConfig = signingConfigs.getByName("debug")
             // Use a local debug keystore to avoid build server issues.
             debugSigningConfig.storeFile = project.getKeystore()
@@ -834,6 +835,7 @@
                 // Sign all the builds (including release) with debug key
                 buildType.signingConfig = debugSigningConfig
             }
+            testBuildType = buildTypeForTests
             project.configureTestConfigGeneration(this)
             project.addAppApkToTestConfigGeneration(androidXExtension)
         }
@@ -853,10 +855,13 @@
                 it.defaultConfig.aarMetadata.minCompileSdk = it.compileSdk
                 it.lint.targetSdk = project.defaultAndroidConfig.targetSdk
                 it.testOptions.targetSdk = project.defaultAndroidConfig.targetSdk
+                // Replace with a public API once available, see b/360392255
+                it.buildTypes.configureEach { buildType ->
+                    if (buildType.name == buildTypeForTests && !project.hasBenchmarkPlugin())
+                        (buildType as TestBuildType).isDebuggable = true
+                }
             }
-            beforeVariants(selector().withBuildType("release")) { variant ->
-                (variant as HasUnitTestBuilder).enableUnitTest = false
-            }
+            beforeVariants(selector().withBuildType("debug")) { variant -> variant.enable = false }
             beforeVariants(selector().all()) { variant ->
                 variant.androidTest.targetSdk = project.defaultAndroidConfig.targetSdk
             }
@@ -1093,9 +1098,12 @@
             }
         }
 
-        project.configureFtlRunner(
+        val componentsExtension =
             project.extensions.getByType(AndroidComponentsExtension::class.java)
-        )
+        project.configureFtlRunner(componentsExtension)
+
+        // If a dependency is missing a debug variant, use release instead.
+        buildTypes.getByName("debug").matchingFallbacks.add("release")
 
         // AGP warns if we use project.buildDir (or subdirs) for CMake's generated
         // build files (ninja build files, CMakeCache.txt, etc.). Use a staging directory that
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
index 607b2c3..ad3c819 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -46,6 +46,7 @@
 import org.gradle.kotlin.dsl.withType
 import org.gradle.work.DisableCachingByDefault
 import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
+import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
 import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
 import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
 import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
@@ -56,7 +57,6 @@
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithHostTests
-import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
 import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
 import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWasmTargetDsl
 import org.jetbrains.kotlin.gradle.targets.js.ir.DefaultIncrementalSyncTask
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/Ktfmt.kt b/buildSrc/private/src/main/kotlin/androidx/build/Ktfmt.kt
index 1b01fa2..202a030 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/Ktfmt.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/Ktfmt.kt
@@ -19,21 +19,20 @@
 import androidx.build.logging.TERMINAL_RED
 import androidx.build.logging.TERMINAL_RESET
 import androidx.build.uptodatedness.cacheEvenIfNoOutputs
-import com.facebook.ktfmt.format.Formatter
-import com.facebook.ktfmt.format.Formatter.format
+import java.io.ByteArrayOutputStream
 import java.io.File
 import java.nio.file.Paths
 import javax.inject.Inject
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.runBlocking
 import org.gradle.api.DefaultTask
 import org.gradle.api.Project
+import org.gradle.api.attributes.java.TargetJvmEnvironment
+import org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.FileCollection
 import org.gradle.api.file.FileTree
 import org.gradle.api.model.ObjectFactory
 import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.Classpath
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.Internal
@@ -43,21 +42,19 @@
 import org.gradle.api.tasks.SkipWhenEmpty
 import org.gradle.api.tasks.TaskAction
 import org.gradle.api.tasks.options.Option
-import org.intellij.lang.annotations.Language
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.gradle.kotlin.dsl.named
+import org.gradle.process.ExecOperations
 
 fun Project.configureKtfmt() {
-    tasks.register("ktFormat", KtfmtFormatTask::class.java)
+    val ktfmtClasspath = getKtfmtConfiguration()
+    tasks.register("ktFormat", KtfmtFormatTask::class.java) { task ->
+        task.ktfmtClasspath.from(ktfmtClasspath)
+    }
 
     val ktCheckTask =
         tasks.register("ktCheck", KtfmtCheckTask::class.java) { task ->
+            task.ktfmtClasspath.from(ktfmtClasspath)
             task.cacheEvenIfNoOutputs()
-            // Workaround for https://github.com/gradle/gradle/issues/29205
-            // Our ktfmt tasks declare "src" as an input, while our KotlinCompile tasks use
-            // something like src/main/java as an input
-            // Currently Gradle can sometimes get confused when loading a parent and child directory
-            // at the same time, so we ask Gradle to avoid running both tasks in parallel
-            task.mustRunAfter(project.tasks.withType(KotlinCompile::class.java))
         }
 
     // afterEvaluate because Gradle's default "check" task doesn't exist yet
@@ -78,11 +75,27 @@
     )
 
 private val ExcludedDirectoryGlobs = ExcludedDirectories.map { "**/$it/**/*.kt" }
+private const val MainClass = "com.facebook.ktfmt.cli.Main"
 private const val InputDir = "src"
 private const val IncludedFiles = "**/*.kt"
 
+private fun Project.getKtfmtConfiguration(): FileCollection {
+    val conf = configurations.detachedConfiguration(dependencies.create(getLibraryByName("ktfmt")))
+    conf.attributes {
+        it.attribute(
+            TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
+            project.objects.named(TargetJvmEnvironment.STANDARD_JVM)
+        )
+    }
+    return files(conf)
+}
+
 @CacheableTask
 abstract class BaseKtfmtTask : DefaultTask() {
+    @get:Inject abstract val execOperations: ExecOperations
+
+    @get:Classpath abstract val ktfmtClasspath: ConfigurableFileCollection
+
     @get:Inject abstract val objects: ObjectFactory
 
     @get:Internal val projectPath: String = project.path
@@ -114,54 +127,39 @@
 
     protected fun runKtfmt(format: Boolean) {
         if (getInputFiles().files.isEmpty()) return
-        runBlocking(Dispatchers.IO) {
-            val result = processInputFiles()
-            val incorrectlyFormatted = result.filter { !it.isCorrectlyFormatted }
-            if (incorrectlyFormatted.isNotEmpty()) {
-                if (format) {
-                    incorrectlyFormatted.forEach { it.input.writeText(it.formattedCode) }
-                } else {
-                    error(
-                        "Found ${incorrectlyFormatted.size} files that are not correctly " +
-                            "formatted:\n" +
-                            incorrectlyFormatted.map { it.input }.joinToString("\n") +
-                            """
-
-                ********************************************************************************
-                You can attempt to automatically fix these issues with:
-                ./gradlew $projectPath:ktFormat
-                ********************************************************************************
-                """
-                                .trimIndent()
-                    )
-                }
-            }
+        val outputStream = ByteArrayOutputStream()
+        execOperations.javaexec { javaExecSpec ->
+            javaExecSpec.standardOutput = outputStream
+            javaExecSpec.mainClass.set(MainClass)
+            javaExecSpec.classpath = ktfmtClasspath
+            javaExecSpec.args = getArgsList(format = format)
+            javaExecSpec.jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
+            overrideDirectory?.let { javaExecSpec.workingDir = it }
+        }
+        val output = outputStream.toString()
+        if (output.isNotEmpty()) {
+            processOutput(output)
+        }
+        if (output.isNotEmpty()) {
+            error(processOutput(output))
         }
     }
 
-    /** Run ktfmt on all the files in [getInputFiles] in parallel. */
-    private suspend fun processInputFiles(): List<KtfmtResult> {
-        return coroutineScope { getInputFiles().files.map { async { processFile(it) } }.awaitAll() }
-    }
+    open fun processOutput(output: String): String =
+        """
+        Failed check for the following files:
+        $output
+    """
+            .trimIndent()
 
-    /** Run ktfmt on the [input] file. */
-    private fun processFile(input: File): KtfmtResult {
-        val originCode = input.readText()
-        val formattedCode = format(Formatter.KOTLINLANG_FORMAT, originCode)
-        return KtfmtResult(
-            input = input,
-            isCorrectlyFormatted = originCode == formattedCode,
-            formattedCode = formattedCode
-        )
+    private fun getArgsList(format: Boolean): List<String> {
+        val arguments = mutableListOf("--kotlinlang-style")
+        if (!format) arguments.add("--dry-run")
+        arguments.addAll(getInputFiles().files.map { it.absolutePath })
+        return arguments
     }
 }
 
-internal data class KtfmtResult(
-    val input: File,
-    val isCorrectlyFormatted: Boolean,
-    @Language("kotlin") val formattedCode: String,
-)
-
 @CacheableTask
 abstract class KtfmtFormatTask : BaseKtfmtTask() {
     init {
@@ -189,6 +187,18 @@
     fun runCheck() {
         runKtfmt(format = false)
     }
+
+    override fun processOutput(output: String): String =
+        """
+                Failed check for the following files:
+                $output
+
+                ********************************************************************************
+                ${TERMINAL_RED}You can automatically fix these issues with:
+                ./gradlew $projectPath:ktFormat$TERMINAL_RESET
+                ********************************************************************************
+            """
+            .trimIndent()
 }
 
 @CacheableTask
@@ -243,33 +253,35 @@
 
     @TaskAction
     fun runCheck() {
-        try {
-            runKtfmt(format = format)
-        } catch (e: IllegalStateException) {
-            val kotlinFiles =
-                files.filter { file ->
-                    val isKotlinFile = file.endsWith(".kt") || file.endsWith(".ktx")
-                    val inExcludedDir =
-                        Paths.get(file).any { subPath ->
-                            ExcludedDirectories.contains(subPath.toString())
-                        }
+        runKtfmt(format = format)
+    }
 
-                    isKotlinFile && !inExcludedDir
-                }
-            error(
-                """
+    override fun processOutput(output: String): String {
+        val kotlinFiles =
+            files.filter { file ->
+                val isKotlinFile = file.endsWith(".kt") || file.endsWith(".ktx")
+                val inExcludedDir =
+                    Paths.get(file).any { subPath ->
+                        ExcludedDirectories.contains(subPath.toString())
+                    }
 
-                ********************************************************************************
-                ${TERMINAL_RED}You can attempt to automatically fix these issues with:
-                ./gradlew :ktCheckFile --format ${kotlinFiles.joinToString(separator = " "){ "--file $it" }}$TERMINAL_RESET
-                ********************************************************************************
-                """
-                    .trimIndent()
-            )
-        }
+                isKotlinFile && !inExcludedDir
+            }
+        return """
+            Failed check for the following files:
+            $output
+
+            ********************************************************************************
+            ${TERMINAL_RED}You can attempt to automatically fix these issues with:
+            ./gradlew :ktCheckFile --format ${kotlinFiles.joinToString(separator = " "){ "--file $it" }}$TERMINAL_RESET
+            ********************************************************************************
+            """
+            .trimIndent()
     }
 }
 
 fun Project.configureKtfmtCheckFile() {
-    tasks.register("ktCheckFile", KtfmtCheckFileTask::class.java)
+    tasks.register("ktCheckFile", KtfmtCheckFileTask::class.java) { task ->
+        task.ktfmtClasspath.from(getKtfmtConfiguration())
+    }
 }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/SourceJarTaskHelper.kt b/buildSrc/private/src/main/kotlin/androidx/build/SourceJarTaskHelper.kt
index 6efe979..ad04c87 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/SourceJarTaskHelper.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/SourceJarTaskHelper.kt
@@ -222,10 +222,10 @@
 
 /**
  * Finds the main compilation for a source set, usually called 'main' but for android we need to
- * search for 'debug' instead.
+ * search for 'release' instead.
  */
 private fun KotlinTarget.mainCompilation() =
-    compilations.findByName(MAIN_COMPILATION_NAME) ?: compilations.getByName("debug")
+    compilations.findByName(MAIN_COMPILATION_NAME) ?: compilations.getByName("release")
 
 /**
  * Writes a metadata file to the given [metadataFile] location for all multiplatform Kotlin source
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt b/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt
index 50f8f8ec..b0b2916 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/clang/KonanBuildService.kt
@@ -35,7 +35,9 @@
 import org.gradle.process.ExecSpec
 import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
 import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
+import org.jetbrains.kotlin.konan.TempFiles
 import org.jetbrains.kotlin.konan.target.Family
+import org.jetbrains.kotlin.konan.target.LinkerArguments
 import org.jetbrains.kotlin.konan.target.LinkerOutputKind
 import org.jetbrains.kotlin.konan.target.Platform
 import org.jetbrains.kotlin.konan.target.PlatformManager
@@ -138,18 +140,22 @@
         val objectFiles = parameters.objectFiles.regularFilePaths()
         val linkedObjectFiles = parameters.linkedObjects.regularFilePaths()
         val linkCommands =
-            platform.linker.finalLinkCommands(
-                objectFiles = objectFiles,
-                executable = outputFile.canonicalPath,
-                libraries = linkedObjectFiles,
-                linkerArgs = linkerFlags,
-                optimize = true,
-                debug = false,
-                kind = LinkerOutputKind.DYNAMIC_LIBRARY,
-                outputDsymBundle = "unused",
-                mimallocEnabled = false,
-                sanitizer = null
-            )
+            with(platform.linker) {
+                LinkerArguments(
+                        TempFiles(),
+                        objectFiles = objectFiles,
+                        executable = outputFile.canonicalPath,
+                        libraries = linkedObjectFiles,
+                        linkerArgs = linkerFlags,
+                        optimize = true,
+                        debug = false,
+                        kind = LinkerOutputKind.DYNAMIC_LIBRARY,
+                        outputDsymBundle = "unused",
+                        mimallocEnabled = false,
+                        sanitizer = null
+                    )
+                    .finalLinkCommands()
+            }
         linkCommands
             .map { it.argsWithExecutable }
             .forEach { args ->
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
index ffb4cc8..144e37e 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
@@ -323,11 +323,11 @@
         }
     }
 
-    // For library modules we only look at the build type debug. The target app project can be
+    // For library modules we only look at the build type release. The target app project can be
     // specified through the androidX extension, through: targetAppProjectForInstrumentationTest
     // and targetAppProjectVariantForInstrumentationTest.
     extensions.findByType(LibraryAndroidComponentsExtension::class.java)?.apply {
-        onVariants(selector().withBuildType("debug")) { variant ->
+        onVariants(selector().withBuildType("release")) { variant ->
             val targetAppProject =
                 androidXExtension.deviceTests.targetAppProject ?: return@onVariants
             val targetAppProjectVariant = androidXExtension.deviceTests.targetAppVariant
@@ -616,7 +616,7 @@
     multiplatformExtension
         ?.targets
         ?.filterIsInstance<KotlinAndroidTarget>()
-        ?.mapNotNull { it.compilations.find { it.name == "debugAndroidTest" } }
+        ?.mapNotNull { it.compilations.find { it.name == "releaseAndroidTest" } }
         ?.flatMap { it.allKotlinSourceSets }
         ?.mapTo(testSourceFileCollections) { it.kotlin.sourceDirectories }
     return testSourceFileCollections
diff --git a/buildSrc/shared-dependencies.gradle b/buildSrc/shared-dependencies.gradle
index 356e5ef..bc37d9f 100644
--- a/buildSrc/shared-dependencies.gradle
+++ b/buildSrc/shared-dependencies.gradle
@@ -39,8 +39,6 @@
     }
     implementation(libs.xerces)
 
-    implementation(libs.ktfmt)
-    implementation(libs.kotlinCoroutinesCore)
     implementation(libs.shadow) // used by BundleInsideHelper.kt
     api(libs.apacheAnt) // used in AarManifestTransformerTask.kt for unziping
     implementation(libs.toml)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
index 577e4db..83c1aa3 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
@@ -18,6 +18,7 @@
 
 import androidx.camera.camera2.pipe.integration.compat.workaround.AutoFlashAEModeDisabler
 import androidx.camera.camera2.pipe.integration.compat.workaround.InactiveSurfaceCloser
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage
 import androidx.camera.camera2.pipe.integration.compat.workaround.MeteringRegionCorrection
 import androidx.camera.camera2.pipe.integration.compat.workaround.TemplateParamsOverride
 import androidx.camera.camera2.pipe.integration.compat.workaround.UseFlashModeTorchFor3aUpdate
@@ -34,6 +35,7 @@
             UseFlashModeTorchFor3aUpdate.Bindings::class,
             UseTorchAsFlash.Bindings::class,
             TemplateParamsOverride.Bindings::class,
+            Lock3ABehaviorWhenCaptureImage.Bindings::class,
         ],
 )
 public abstract class CameraCompatModule
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
index 7988db32..81e63e1 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
@@ -240,6 +240,14 @@
         ) {
             quirks.add(ImageCaptureFailedForVideoSnapshotQuirk())
         }
+        if (
+            quirkSettings.shouldEnableQuirk(
+                LockAeAndCaptureImageBreakCameraQuirk::class.java,
+                LockAeAndCaptureImageBreakCameraQuirk.isEnabled(cameraMetadata)
+            )
+        ) {
+            quirks.add(LockAeAndCaptureImageBreakCameraQuirk())
+        }
 
         Quirks(quirks).also {
             Logger.d(TAG, "camera2-pipe-integration CameraQuirks = " + Quirks.toString(it))
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/LockAeAndCaptureImageBreakCameraQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/LockAeAndCaptureImageBreakCameraQuirk.kt
new file mode 100644
index 0000000..95ba980
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/LockAeAndCaptureImageBreakCameraQuirk.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2024 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.camera2.pipe.integration.compat.quirk
+
+import android.annotation.SuppressLint
+import android.hardware.camera2.CameraCharacteristics.LENS_FACING
+import android.hardware.camera2.CameraMetadata.LENS_FACING_BACK
+import android.os.Build
+import androidx.camera.camera2.pipe.CameraMetadata
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage
+import androidx.camera.core.impl.Quirk
+
+/**
+ * QuirkSummary
+ * - Bug Id: b/360106037
+ * - Description: Quirk indicating that locking AE (Auto Exposure) and taking pictures can lead to
+ *   an abnormal camera service state on Pixel 3 back camera. Although the picture is successfully
+ *   taken, the camera service becomes unresponsive without any error callbacks. Reopening the
+ *   camera can restore its functionality.
+ * - Device(s): Pixel 3.
+ *
+ * @see Lock3ABehaviorWhenCaptureImage
+ */
+@SuppressLint("CameraXQuirksClassDetector")
+public class LockAeAndCaptureImageBreakCameraQuirk : Quirk {
+
+    public companion object {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+            return isPixel3 && cameraMetadata[LENS_FACING] == LENS_FACING_BACK
+        }
+
+        private val isPixel3: Boolean
+            get() = "Pixel 3".equals(Build.MODEL, ignoreCase = true)
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImage.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImage.kt
new file mode 100644
index 0000000..f893efd
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImage.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2024 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.camera2.pipe.integration.compat.workaround
+
+import androidx.camera.camera2.pipe.Lock3ABehavior
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.LockAeAndCaptureImageBreakCameraQuirk
+import dagger.Module
+import dagger.Provides
+
+/**
+ * Provides customized 3A lock behaviors before capturing an image.
+ *
+ * @property hasAeLockBehavior Indicates whether there is a specific AE (Auto Exposure) lock
+ *   behavior defined. If true, the [aeLockBehavior] will be used; otherwise, the default AE
+ *   behavior will be applied.
+ * @property aeLockBehavior The specific AE lock behavior to apply if [hasAeLockBehavior] is true.
+ *   If null and [hasAeLockBehavior] is true, AE lock will be effectively disabled.
+ * @property hasAfLockBehavior Indicates whether there is a specific AF (Auto Focus) lock behavior
+ *   defined. If true, the [afLockBehavior] will be used; otherwise, the default AF behavior will be
+ *   applied.
+ * @property afLockBehavior The specific AF lock behavior to apply if [hasAfLockBehavior] is true.
+ *   If null and [hasAfLockBehavior] is true, AF lock will be effectively disabled.
+ * @property hasAwbLockBehavior Indicates whether there is a specific AWB (Auto White Balance) lock
+ *   behavior defined. If true, the [awbLockBehavior] will be used; otherwise, the default AWB
+ *   behavior will be applied.
+ * @property awbLockBehavior The specific AWB lock behavior to apply if [hasAwbLockBehavior] is
+ *   true. If null and [hasAwbLockBehavior] is true, AWB lock will be effectively disabled.
+ * @see LockAeAndCaptureImageBreakCameraQuirk
+ */
+public class Lock3ABehaviorWhenCaptureImage(
+    private val hasAeLockBehavior: Boolean = false,
+    private val aeLockBehavior: Lock3ABehavior? = null,
+    private val hasAfLockBehavior: Boolean = false,
+    private val afLockBehavior: Lock3ABehavior? = null,
+    private val hasAwbLockBehavior: Boolean = false,
+    private val awbLockBehavior: Lock3ABehavior? = null,
+) {
+
+    /**
+     * Gets customized 3A lock behaviors, using provided defaults if no specific behavior is set.
+     *
+     * This method checks the `has*LockBehavior` properties to determine if a custom behavior is
+     * defined for each 3A lock type (AE, AF, AWB). If a custom behavior is defined, it will be
+     * returned; otherwise, the corresponding `default*Behavior` will be used.
+     *
+     * @param defaultAeBehavior Default AE lock behavior if none is specified.
+     * @param defaultAfBehavior Default AF lock behavior if none is specified.
+     * @param defaultAwbBehavior Default AWB lock behavior if none is specified.
+     * @return A Triple containing the customized AE, AF, and AWB lock behaviors.
+     */
+    public fun getLock3ABehaviors(
+        defaultAeBehavior: Lock3ABehavior? = null,
+        defaultAfBehavior: Lock3ABehavior? = null,
+        defaultAwbBehavior: Lock3ABehavior? = null
+    ): Triple<Lock3ABehavior?, Lock3ABehavior?, Lock3ABehavior?> =
+        Triple(
+            if (hasAeLockBehavior) aeLockBehavior else defaultAeBehavior,
+            if (hasAfLockBehavior) afLockBehavior else defaultAfBehavior,
+            if (hasAwbLockBehavior) awbLockBehavior else defaultAwbBehavior
+        )
+
+    @Module
+    public abstract class Bindings {
+        public companion object {
+            @Provides
+            public fun provideLock3ABehaviorBeforeCaptureImage(
+                cameraQuirks: CameraQuirks
+            ): Lock3ABehaviorWhenCaptureImage =
+                if (
+                    cameraQuirks.quirks.contains(LockAeAndCaptureImageBreakCameraQuirk::class.java)
+                ) {
+                    doNotLockAe3ABehavior
+                } else {
+                    noCustomizedLock3ABehavior
+                }
+        }
+    }
+
+    public companion object {
+        public val noCustomizedLock3ABehavior: Lock3ABehaviorWhenCaptureImage by lazy {
+            Lock3ABehaviorWhenCaptureImage()
+        }
+
+        public val doNotLockAe3ABehavior: Lock3ABehaviorWhenCaptureImage by lazy {
+            Lock3ABehaviorWhenCaptureImage(
+                hasAeLockBehavior = true,
+                aeLockBehavior = null // Explicitly disable AE lock
+            )
+        }
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
index 372696c..1d21a90 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
@@ -49,6 +49,7 @@
 import androidx.camera.camera2.pipe.core.Log.debug
 import androidx.camera.camera2.pipe.core.Log.info
 import androidx.camera.camera2.pipe.integration.adapter.CaptureConfigAdapter
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage
 import androidx.camera.camera2.pipe.integration.compat.workaround.UseTorchAsFlash
 import androidx.camera.camera2.pipe.integration.compat.workaround.isFlashAvailable
 import androidx.camera.camera2.pipe.integration.compat.workaround.shouldStopRepeatingBeforeCapture
@@ -110,6 +111,7 @@
     private val threads: UseCaseThreads,
     private val requestListener: ComboRequestListener,
     private val useTorchAsFlash: UseTorchAsFlash,
+    private val lock3ABehaviorWhenCaptureImage: Lock3ABehaviorWhenCaptureImage,
     cameraProperties: CameraProperties,
     private val useCaseCameraState: UseCaseCameraState,
     useCaseGraphConfig: UseCaseGraphConfig,
@@ -378,10 +380,16 @@
         graph
             .acquireSession()
             .use {
+                val (aeLockBehavior, afLockBehavior, awbLockBehavior) =
+                    lock3ABehaviorWhenCaptureImage.getLock3ABehaviors(
+                        defaultAeBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
+                        defaultAfBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
+                        defaultAwbBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
+                    )
                 it.lock3A(
-                    aeLockBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
-                    afLockBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
-                    awbLockBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
+                    aeLockBehavior = aeLockBehavior,
+                    afLockBehavior = afLockBehavior,
+                    awbLockBehavior = awbLockBehavior,
                     convergedTimeLimitNs = convergedTimeLimitNs,
                     lockedTimeLimitNs = CHECK_3A_TIMEOUT_IN_NS
                 )
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImageTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImageTest.kt
new file mode 100644
index 0000000..206bca8
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImageTest.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2024 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.camera2.pipe.integration.compat.workaround
+
+import androidx.camera.camera2.pipe.Lock3ABehavior
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+class Lock3ABehaviorWhenCaptureImageTest {
+
+    @Test
+    fun getLock3ABehaviors_noCustomBehaviors_returnsDefaults() {
+        val lock3ABehavior = Lock3ABehaviorWhenCaptureImage()
+        val defaultAeBehavior = null
+        val defaultAfBehavior = Lock3ABehavior.AFTER_NEW_SCAN
+        val defaultAwbBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN
+
+        val (ae, af, awb) =
+            lock3ABehavior.getLock3ABehaviors(
+                defaultAeBehavior,
+                defaultAfBehavior,
+                defaultAwbBehavior
+            )
+
+        assertThat(ae).isEqualTo(defaultAeBehavior)
+        assertThat(af).isEqualTo(defaultAfBehavior)
+        assertThat(awb).isEqualTo(defaultAwbBehavior)
+    }
+
+    @Test
+    fun getLock3ABehaviors_withCustomBehaviors_returnsCustom() {
+        val customAeBehavior = null
+        val customAfBehavior = Lock3ABehavior.AFTER_NEW_SCAN
+        val customAwbBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN
+        val lock3ABehavior =
+            Lock3ABehaviorWhenCaptureImage(
+                hasAeLockBehavior = true,
+                aeLockBehavior = customAeBehavior,
+                hasAfLockBehavior = true,
+                afLockBehavior = customAfBehavior,
+                hasAwbLockBehavior = true,
+                awbLockBehavior = customAwbBehavior
+            )
+
+        val (ae, af, awb) = lock3ABehavior.getLock3ABehaviors() // No defaults needed
+
+        assertThat(ae).isEqualTo(customAeBehavior)
+        assertThat(af).isEqualTo(customAfBehavior)
+        assertThat(awb).isEqualTo(customAwbBehavior)
+    }
+
+    @Test
+    fun getLock3ABehaviors_mixedBehaviors_returnsCorrectly() {
+        val customAfBehavior = Lock3ABehavior.AFTER_NEW_SCAN
+        val lock3ABehavior =
+            Lock3ABehaviorWhenCaptureImage(
+                hasAfLockBehavior = true,
+                afLockBehavior = customAfBehavior
+            )
+        val defaultAeBehavior = null
+        val defaultAwbBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN
+
+        val (ae, af, awb) =
+            lock3ABehavior.getLock3ABehaviors(
+                defaultAeBehavior,
+                defaultAwbBehavior = defaultAwbBehavior
+            )
+
+        assertThat(ae).isEqualTo(defaultAeBehavior) // Default used
+        assertThat(af).isEqualTo(customAfBehavior) // Custom used
+        assertThat(awb).isEqualTo(defaultAwbBehavior) // Default used
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
index 35fb7e0..f968130 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
@@ -48,11 +48,15 @@
 import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
 import androidx.camera.camera2.pipe.integration.compat.workaround.AeFpsRange
 import androidx.camera.camera2.pipe.integration.compat.workaround.CapturePipelineTorchCorrection
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage.Companion.doNotLockAe3ABehavior
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage.Companion.noCustomizedLock3ABehavior
 import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpAutoFlashAEModeDisabler
 import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpTemplateParamsOverride
 import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseFlashModeTorchFor3aUpdate
 import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseTorchAsFlash
 import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
+import androidx.camera.camera2.pipe.integration.compat.workaround.UseTorchAsFlash
 import androidx.camera.camera2.pipe.integration.compat.workaround.UseTorchAsFlashImpl
 import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
 import androidx.camera.camera2.pipe.integration.interop.CaptureRequestOptions
@@ -153,6 +157,9 @@
             var waitForAwbAtLock3AForCapture: Boolean = false
 
             var cancelAfAtUnlock3AForCapture: Boolean = false
+            var aeLockBehavior: Lock3ABehavior? = null
+            var afLockBehavior: Lock3ABehavior? = null
+            var awbLockBehavior: Lock3ABehavior? = null
 
             override suspend fun lock3A(
                 aeMode: AeMode?,
@@ -171,6 +178,9 @@
                 convergedTimeLimitNs: Long,
                 lockedTimeLimitNs: Long
             ): Deferred<Result3A> {
+                this.aeLockBehavior = aeLockBehavior
+                this.afLockBehavior = afLockBehavior
+                this.awbLockBehavior = awbLockBehavior
                 lock3ASemaphore.release()
                 return CompletableDeferred(Result3A(Result3A.Status.OK))
             }
@@ -359,19 +369,7 @@
                 templateParamsOverride = NoOpTemplateParamsOverride,
             )
 
-        capturePipeline =
-            CapturePipelineImpl(
-                configAdapter = fakeCaptureConfigAdapter,
-                cameraProperties = fakeCameraProperties,
-                requestListener = comboRequestListener,
-                threads = fakeUseCaseThreads,
-                torchControl = torchControl,
-                useCaseGraphConfig = fakeUseCaseGraphConfig,
-                useCaseCameraState = fakeUseCaseCameraState,
-                useTorchAsFlash = NotUseTorchAsFlash,
-                sessionProcessorManager = null,
-                flashControl = flashControl,
-            )
+        capturePipeline = createCapturePipeline()
     }
 
     @After
@@ -475,19 +473,7 @@
 
     private suspend fun TestScope.withTorchAsFlashQuirk_shouldOpenTorch(imageCaptureMode: Int) {
         // Arrange.
-        capturePipeline =
-            CapturePipelineImpl(
-                configAdapter = fakeCaptureConfigAdapter,
-                cameraProperties = fakeCameraProperties,
-                requestListener = comboRequestListener,
-                threads = fakeUseCaseThreads,
-                torchControl = torchControl,
-                useCaseGraphConfig = fakeUseCaseGraphConfig,
-                useCaseCameraState = fakeUseCaseCameraState,
-                useTorchAsFlash = UseTorchAsFlashImpl,
-                sessionProcessorManager = null,
-                flashControl = flashControl,
-            )
+        capturePipeline = createCapturePipeline(useTorchAsFlash = UseTorchAsFlashImpl)
 
         val requestList = mutableListOf<Request>()
         fakeCameraGraphSession.requestHandler = { requests -> requestList.addAll(requests) }
@@ -602,22 +588,69 @@
     @Test
     fun miniLatency_flashRequired_withFlashTypeTorch_shouldLock3A(): Unit = runTest {
         withFlashTypeTorch_shouldLock3A(
+            capturePipeline,
             ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
-            ImageCapture.FLASH_MODE_ON
+            ImageCapture.FLASH_MODE_ON,
+            expectedLock3ABehaviors =
+                Triple(
+                    Lock3ABehavior.AFTER_CURRENT_SCAN,
+                    Lock3ABehavior.AFTER_CURRENT_SCAN,
+                    Lock3ABehavior.AFTER_CURRENT_SCAN
+                )
         )
     }
 
     @Test
+    fun miniLatency_flashRequired_withFlashTypeTorch_doNotLockAe3ABehavior_shouldLock3A(): Unit =
+        runTest {
+            val capturePipeline =
+                createCapturePipeline(lock3ABehaviorWhenCaptureImage = doNotLockAe3ABehavior)
+            withFlashTypeTorch_shouldLock3A(
+                capturePipeline,
+                ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
+                ImageCapture.FLASH_MODE_ON,
+                expectedLock3ABehaviors =
+                    Triple(
+                        null,
+                        Lock3ABehavior.AFTER_CURRENT_SCAN,
+                        Lock3ABehavior.AFTER_CURRENT_SCAN
+                    )
+            )
+        }
+
+    @Test
     fun maxQuality_withFlashTypeTorch_shouldLock3A(): Unit = runTest {
         withFlashTypeTorch_shouldLock3A(
+            capturePipeline,
             ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY,
-            ImageCapture.FLASH_MODE_OFF
+            ImageCapture.FLASH_MODE_OFF,
+            expectedLock3ABehaviors =
+                Triple(
+                    Lock3ABehavior.AFTER_CURRENT_SCAN,
+                    Lock3ABehavior.AFTER_CURRENT_SCAN,
+                    Lock3ABehavior.AFTER_CURRENT_SCAN
+                )
+        )
+    }
+
+    @Test
+    fun maxQuality_withFlashTypeTorch_doNotLockAe3ABehavior_shouldLock3A(): Unit = runTest {
+        val capturePipeline =
+            createCapturePipeline(lock3ABehaviorWhenCaptureImage = doNotLockAe3ABehavior)
+        withFlashTypeTorch_shouldLock3A(
+            capturePipeline,
+            ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY,
+            ImageCapture.FLASH_MODE_OFF,
+            expectedLock3ABehaviors =
+                Triple(null, Lock3ABehavior.AFTER_CURRENT_SCAN, Lock3ABehavior.AFTER_CURRENT_SCAN)
         )
     }
 
     private suspend fun TestScope.withFlashTypeTorch_shouldLock3A(
+        capturePipeline: CapturePipeline,
         imageCaptureMode: Int,
-        flashMode: Int
+        flashMode: Int,
+        expectedLock3ABehaviors: Triple<Lock3ABehavior?, Lock3ABehavior?, Lock3ABehavior?>,
     ) {
         // Arrange.
         val requestList = mutableListOf<Request>()
@@ -636,6 +669,10 @@
         // Assert 1, should call lock3A, but not call unlock3A (before capturing is finished).
         assertThat(fakeCameraGraphSession.lock3ASemaphore.tryAcquire(this)).isTrue()
         assertThat(fakeCameraGraphSession.unlock3ASemaphore.tryAcquire(this)).isFalse()
+        // Ensure correct Lock3ABehaviors are set.
+        assertThat(fakeCameraGraphSession.aeLockBehavior).isEqualTo(expectedLock3ABehaviors.first)
+        assertThat(fakeCameraGraphSession.afLockBehavior).isEqualTo(expectedLock3ABehaviors.second)
+        assertThat(fakeCameraGraphSession.awbLockBehavior).isEqualTo(expectedLock3ABehaviors.third)
 
         // Complete the capture request.
         assertThat(fakeCameraGraphSession.submitSemaphore.tryAcquire(this)).isTrue()
@@ -1221,6 +1258,24 @@
         assertThat(screenFlash.awaitClear(3000)).isTrue()
     }
 
+    private fun createCapturePipeline(
+        useTorchAsFlash: UseTorchAsFlash = NotUseTorchAsFlash,
+        lock3ABehaviorWhenCaptureImage: Lock3ABehaviorWhenCaptureImage = noCustomizedLock3ABehavior
+    ) =
+        CapturePipelineImpl(
+            configAdapter = fakeCaptureConfigAdapter,
+            cameraProperties = fakeCameraProperties,
+            requestListener = comboRequestListener,
+            threads = fakeUseCaseThreads,
+            torchControl = torchControl,
+            useCaseGraphConfig = fakeUseCaseGraphConfig,
+            useCaseCameraState = fakeUseCaseCameraState,
+            useTorchAsFlash = useTorchAsFlash,
+            lock3ABehaviorWhenCaptureImage = lock3ABehaviorWhenCaptureImage,
+            sessionProcessorManager = null,
+            flashControl = flashControl,
+        )
+
     // TODO(wenhungteng@): Porting overrideAeModeForStillCapture_quirkAbsent_notOverride,
     //  overrideAeModeForStillCapture_aePrecaptureStarted_override,
     //  overrideAeModeForStillCapture_aePrecaptureFinish_notOverride,
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
index a62aef8..5698804 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
@@ -23,6 +23,7 @@
 import androidx.camera.camera2.pipe.integration.adapter.CaptureConfigAdapter
 import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
 import androidx.camera.camera2.pipe.integration.adapter.ZslControlNoOpImpl
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage.Companion.noCustomizedLock3ABehavior
 import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpTemplateParamsOverride
 import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseFlashModeTorchFor3aUpdate
 import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseTorchAsFlash
@@ -435,6 +436,7 @@
                         useCaseGraphConfig = fakeUseCaseGraphConfig,
                         useCaseCameraState = fakeUseCaseCameraState,
                         useTorchAsFlash = NotUseTorchAsFlash,
+                        lock3ABehaviorWhenCaptureImage = noCustomizedLock3ABehavior,
                         sessionProcessorManager = null,
                         flashControl =
                             FlashControl(
diff --git a/camera/camera-compose/api/current.txt b/camera/camera-compose/api/current.txt
new file mode 100644
index 0000000..18c7f03
--- /dev/null
+++ b/camera/camera-compose/api/current.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.camera.compose {
+
+  public final class CameraXViewfinderKt {
+    method @androidx.compose.runtime.Composable public static void CameraXViewfinder(androidx.camera.core.SurfaceRequest surfaceRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.camera.viewfinder.surface.ImplementationMode implementationMode, optional androidx.camera.viewfinder.compose.MutableCoordinateTransformer? coordinateTransformer);
+  }
+
+}
+
diff --git a/activity/activity-compose/api/res-1.10.0-beta01.txt b/camera/camera-compose/api/res-current.txt
similarity index 100%
rename from activity/activity-compose/api/res-1.10.0-beta01.txt
rename to camera/camera-compose/api/res-current.txt
diff --git a/camera/camera-compose/api/restricted_current.txt b/camera/camera-compose/api/restricted_current.txt
new file mode 100644
index 0000000..18c7f03
--- /dev/null
+++ b/camera/camera-compose/api/restricted_current.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.camera.compose {
+
+  public final class CameraXViewfinderKt {
+    method @androidx.compose.runtime.Composable public static void CameraXViewfinder(androidx.camera.core.SurfaceRequest surfaceRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.camera.viewfinder.surface.ImplementationMode implementationMode, optional androidx.camera.viewfinder.compose.MutableCoordinateTransformer? coordinateTransformer);
+  }
+
+}
+
diff --git a/camera/camera-compose/build.gradle b/camera/camera-compose/build.gradle
new file mode 100644
index 0000000..337ad6c
--- /dev/null
+++ b/camera/camera-compose/build.gradle
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/**
+ * This file was created using the `create_project.py` script located in the
+ * `<AndroidX root>/development/project-creator` directory.
+ *
+ * Please use that script when creating a new project, rather than copying an existing project and
+ * modifying its settings.
+ */
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("AndroidXComposePlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(project(":camera:camera-core"))
+    // TODO(b/357895362): Switch to pinned dependencies when stable is released
+    api(project(":camera:viewfinder:viewfinder-compose"))
+    api(project(":camera:viewfinder:viewfinder-core"))
+    implementation("androidx.compose.foundation:foundation-layout:1.6.1")
+    implementation("androidx.compose.foundation:foundation:1.6.1")
+    implementation("androidx.compose.runtime:runtime:1.6.1")
+    implementation("androidx.core:core-ktx:1.12.0")
+
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(project(":camera:camera-camera2"))
+    androidTestImplementation(project(":camera:camera-camera2-pipe-integration"))
+    androidTestImplementation(project(":camera:camera-lifecycle"))
+    androidTestImplementation(project(":camera:camera-testing")) {
+        // Ensure camera-testing does not pull in androidx.test dependencies
+        exclude(group:"androidx.test")
+    }
+    androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.6.1")
+    androidTestImplementation("androidx.compose.ui:ui-test-manifest:1.6.1")
+}
+
+android {
+    compileSdk 35
+    namespace "androidx.camera.compose"
+    // TODO(b/349411310): Remove once we can update runtime to 1.7.0
+    experimentalProperties["android.lint.useK2Uast"] = false
+}
+
+androidx {
+    name = "Camera Compose"
+    type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
+    inceptionYear = "2024"
+    description = "Jetpack Compose tools for users of the Jetpack Camera camera-core library"
+}
diff --git a/camera/camera-compose/samples/build.gradle b/camera/camera-compose/samples/build.gradle
new file mode 100644
index 0000000..a2d5268
--- /dev/null
+++ b/camera/camera-compose/samples/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/**
+ * This file was created using the `create_project.py` script located in the
+ * `<AndroidX root>/development/project-creator` directory.
+ *
+ * Please use that script when creating a new project, rather than copying an existing project and
+ * modifying its settings.
+ */
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("AndroidXComposePlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api("androidx.annotation:annotation:1.8.1")
+    implementation(libs.kotlinStdlib)
+    implementation(project(":camera:camera-compose"))
+    implementation(project(":camera:camera-core"))
+    implementation("androidx.compose.foundation:foundation:1.6.8")
+    implementation("androidx.compose.runtime:runtime:1.6.8")
+    implementation("androidx.compose.ui:ui:1.6.8")
+    implementation("androidx.lifecycle:lifecycle-viewmodel:2.8.4")
+    compileOnly(project(":annotation:annotation-sampled"))
+}
+
+android {
+    compileSdk 35
+    namespace "androidx.camera.compose.samples"
+}
+
+androidx {
+    name = "Camera Compose Samples"
+    type = LibraryType.SAMPLES
+    inceptionYear = "2024"
+    description = "Contains sample code for the Androidx Camera Compose library"
+}
diff --git a/camera/camera-compose/samples/src/main/java/androidx/camera/compose/samples/CameraXViewfinderSamples.kt b/camera/camera-compose/samples/src/main/java/androidx/camera/compose/samples/CameraXViewfinderSamples.kt
new file mode 100644
index 0000000..82d2ba0
--- /dev/null
+++ b/camera/camera-compose/samples/src/main/java/androidx/camera/compose/samples/CameraXViewfinderSamples.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2024 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.compose.samples
+
+import android.util.Size
+import androidx.annotation.Sampled
+import androidx.camera.compose.CameraXViewfinder
+import androidx.camera.core.Preview
+import androidx.camera.core.SurfaceRequest
+import androidx.camera.viewfinder.compose.MutableCoordinateTransformer
+import androidx.camera.viewfinder.surface.ImplementationMode
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.lifecycle.ViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+@Suppress("unused", "UNUSED_PARAMETER")
+@Sampled
+fun CameraXViewfinderSample() {
+    class PreviewViewModel : ViewModel() {
+        private val _surfaceRequests = MutableStateFlow<SurfaceRequest?>(null)
+
+        val surfaceRequests: StateFlow<SurfaceRequest?>
+            get() = _surfaceRequests.asStateFlow()
+
+        private fun produceSurfaceRequests(previewUseCase: Preview) {
+            // Always publish new SurfaceRequests from Preview
+            previewUseCase.setSurfaceProvider { newSurfaceRequest ->
+                _surfaceRequests.value = newSurfaceRequest
+            }
+        }
+
+        fun focusOnPoint(surfaceBounds: Size, x: Float, y: Float) {
+            // Create point for CameraX's CameraControl.startFocusAndMetering() and submit...
+        }
+
+        // ...
+    }
+
+    @Composable
+    fun MyCameraViewfinder(viewModel: PreviewViewModel, modifier: Modifier = Modifier) {
+        val currentSurfaceRequest: SurfaceRequest? by viewModel.surfaceRequests.collectAsState()
+
+        currentSurfaceRequest?.let { surfaceRequest ->
+
+            // CoordinateTransformer for transforming from Offsets to Surface coordinates
+            val coordinateTransformer = remember { MutableCoordinateTransformer() }
+
+            CameraXViewfinder(
+                surfaceRequest = surfaceRequest,
+                implementationMode = ImplementationMode.EXTERNAL, // Can also use EMBEDDED
+                modifier =
+                    modifier.pointerInput(Unit) {
+                        detectTapGestures {
+                            with(coordinateTransformer) {
+                                val surfaceCoords = it.transform()
+                                viewModel.focusOnPoint(
+                                    surfaceRequest.resolution,
+                                    surfaceCoords.x,
+                                    surfaceCoords.y
+                                )
+                            }
+                        }
+                    },
+                coordinateTransformer = coordinateTransformer
+            )
+        }
+    }
+}
diff --git a/camera/camera-compose/src/androidTest/java/androidx.camera.compose/CameraXViewfinderTest.kt b/camera/camera-compose/src/androidTest/java/androidx.camera.compose/CameraXViewfinderTest.kt
new file mode 100644
index 0000000..3c53f75
--- /dev/null
+++ b/camera/camera-compose/src/androidTest/java/androidx.camera.compose/CameraXViewfinderTest.kt
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2024 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.compose
+
+import android.content.Context
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.pipe.integration.CameraPipeConfig
+import androidx.camera.core.Camera
+import androidx.camera.core.CameraXConfig
+import androidx.camera.core.Preview
+import androidx.camera.core.SurfaceRequest
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.testing.impl.CameraPipeConfigTestRule
+import androidx.camera.testing.impl.CameraUtil
+import androidx.camera.testing.impl.CameraUtil.PreTestCameraIdList
+import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
+import androidx.camera.viewfinder.surface.ImplementationMode
+import androidx.compose.runtime.collectAsState
+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.testTag
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.concurrent.futures.await
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.resume
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.produceIn
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeout
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class CameraXViewfinderTest(private val implName: String, private val cameraConfig: CameraXConfig) {
+    @get:Rule
+    val cameraPipeConfigTestRule =
+        CameraPipeConfigTestRule(
+            active = implName == CameraPipeConfig::class.simpleName,
+        )
+
+    @get:Rule
+    val useCamera =
+        CameraUtil.grantCameraPermissionAndPreTestAndPostTest(PreTestCameraIdList(cameraConfig))
+
+    @get:Rule val composeTest = createComposeRule()
+
+    @Test
+    fun viewfinderIsDisplayed_withValidSurfaceRequest() = runViewfinderTest {
+        composeTest.setContent {
+            val currentSurfaceRequest: SurfaceRequest? by surfaceRequests.collectAsState()
+            currentSurfaceRequest?.let { surfaceRequest ->
+                CameraXViewfinder(
+                    surfaceRequest = surfaceRequest,
+                    modifier = Modifier.testTag(CAMERAX_VIEWFINDER_TEST_TAG)
+                )
+            }
+        }
+
+        // Start the camera
+        startCamera()
+
+        // Wait for first SurfaceRequest
+        surfaceRequests.filterNotNull().first()
+
+        composeTest.awaitIdle()
+
+        // CameraXViewfinder should now have a child Viewfinder
+        composeTest
+            .onNodeWithTag(CAMERAX_VIEWFINDER_TEST_TAG)
+            .assertIsDisplayed()
+            .assert(SemanticsMatcher.hasChild())
+    }
+
+    @OptIn(DelicateCoroutinesApi::class)
+    @Test
+    fun changingImplementation_sendsNewSurfaceRequest() = runViewfinderTest {
+        var implementationMode: ImplementationMode by mutableStateOf(ImplementationMode.EXTERNAL)
+        composeTest.setContent {
+            val currentSurfaceRequest: SurfaceRequest? by surfaceRequests.collectAsState()
+            currentSurfaceRequest?.let { surfaceRequest ->
+                CameraXViewfinder(
+                    surfaceRequest = surfaceRequest,
+                    implementationMode = implementationMode,
+                    modifier = Modifier.testTag(CAMERAX_VIEWFINDER_TEST_TAG)
+                )
+            }
+        }
+
+        // Collect expected number of SurfaceRequests for 2 mode changes
+        val surfaceRequestSequence = surfaceRequests.filterNotNull().take(3).produceIn(this)
+
+        // Start the camera
+        startCamera()
+
+        // Swap implementation modes twice to produce 3 SurfaceRequests
+        val allSurfaceRequests = buildList {
+            for (surfaceRequest in surfaceRequestSequence) {
+                add(surfaceRequest)
+                composeTest.awaitIdle()
+
+                if (!surfaceRequestSequence.isClosedForReceive) {
+                    // Changing the implementation mode will invalidate the previous SurfaceRequest
+                    // and cause Preview to send a new SurfaceRequest
+                    implementationMode = implementationMode.swapMode()
+                    composeTest.awaitIdle()
+                }
+            }
+        }
+
+        assertThat(allSurfaceRequests.size).isEqualTo(3)
+        assertThat(allSurfaceRequests).containsNoDuplicates()
+    }
+
+    @Test
+    fun cancelledSurfaceRequest_doesNotInstantiateViewfinder() = runViewfinderTest {
+        // Start the camera
+        startCamera()
+
+        // Wait for first SurfaceRequest
+        val surfaceRequest = surfaceRequests.filterNotNull().first()
+
+        // Reset surface provider to cause cancellation of the last SurfaceRequest
+        resetPreviewSurfaceProvider()
+
+        // Ensure the SurfaceRequest is cancelled
+        surfaceRequest.awaitCancellation()
+
+        // Pass on cancelled SurfaceRequest to CameraXViewfinder
+        composeTest.setContent {
+            CameraXViewfinder(
+                surfaceRequest = surfaceRequest,
+                modifier = Modifier.testTag(CAMERAX_VIEWFINDER_TEST_TAG)
+            )
+        }
+
+        composeTest.awaitIdle()
+
+        // Viewfinder should not be displayed since SurfaceRequest was cancelled
+        composeTest.onNodeWithTag(CAMERAX_VIEWFINDER_TEST_TAG).assertIsNotDisplayed()
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun data() =
+            listOf(
+                arrayOf(Camera2Config::class.simpleName, Camera2Config.defaultConfig()),
+                arrayOf(CameraPipeConfig::class.simpleName, CameraPipeConfig.defaultConfig())
+            )
+
+        private const val CAMERAX_VIEWFINDER_TEST_TAG = "CameraXViewfinderTestTag"
+    }
+
+    private inline fun runViewfinderTest(crossinline block: suspend PreviewTestScope.() -> Unit) =
+        runBlocking {
+            val context = ApplicationProvider.getApplicationContext<Context>()
+            val cameraProvider =
+                withTimeout(10.seconds) {
+                    ProcessCameraProvider.configureInstance(cameraConfig)
+                    ProcessCameraProvider.getInstance(context).await()
+                }
+
+            var fakeLifecycleOwner: FakeLifecycleOwner? = null
+            try {
+                val preview = Preview.Builder().build()
+                val surfaceRequests = MutableStateFlow<SurfaceRequest?>(null)
+                val resetPreviewSurfaceProvider =
+                    suspend {
+                            withContext(Dispatchers.Main) {
+                                // Reset the surface provider to a new lambda that will continue to
+                                // publish to surfaceRequests
+                                preview.setSurfaceProvider { surfaceRequest ->
+                                    surfaceRequests.value = surfaceRequest
+                                }
+                            }
+                        }
+                        .also { it.invoke() }
+
+                val startCamera = suspend {
+                    withContext(Dispatchers.Main) {
+                        val lifecycleOwner =
+                            FakeLifecycleOwner().apply {
+                                startAndResume()
+                                fakeLifecycleOwner = this
+                            }
+
+                        val firstAvailableCameraSelector =
+                            cameraProvider.availableCameraInfos
+                                .asSequence()
+                                .map { it.cameraSelector }
+                                .first()
+                        cameraProvider.bindToLifecycle(
+                            lifecycleOwner,
+                            firstAvailableCameraSelector,
+                            preview
+                        )
+                    }
+                }
+
+                with(
+                    PreviewTestScope(
+                        surfaceRequests = surfaceRequests.asStateFlow(),
+                        resetPreviewSurfaceProvider = resetPreviewSurfaceProvider,
+                        startCamera = startCamera,
+                        coroutineContext = coroutineContext
+                    )
+                ) {
+                    block()
+                }
+            } finally {
+                fakeLifecycleOwner?.apply {
+                    withContext(Dispatchers.Main) {
+                        pauseAndStop()
+                        destroy()
+                    }
+                }
+                withTimeout(30.seconds) { cameraProvider.shutdownAsync().await() }
+            }
+        }
+
+    private data class PreviewTestScope(
+        val surfaceRequests: StateFlow<SurfaceRequest?>,
+        val resetPreviewSurfaceProvider: suspend () -> Unit,
+        val startCamera: suspend () -> Camera,
+        override val coroutineContext: CoroutineContext
+    ) : CoroutineScope
+}
+
+private fun ImplementationMode.swapMode(): ImplementationMode {
+    return when (this) {
+        ImplementationMode.EXTERNAL -> ImplementationMode.EMBEDDED
+        ImplementationMode.EMBEDDED -> ImplementationMode.EXTERNAL
+    }
+}
+
+private fun SemanticsMatcher.Companion.hasChild() =
+    SemanticsMatcher("Has child") { node -> node.children.isNotEmpty() }
+
+private suspend fun SurfaceRequest.awaitCancellation(): Unit = suspendCancellableCoroutine { cont ->
+    addRequestCancellationListener(Runnable::run) { cont.resume(Unit) }
+}
diff --git a/camera/camera-compose/src/main/java/androidx/camera/androidx-camera-camera-compose-documentation.md b/camera/camera-compose/src/main/java/androidx/camera/androidx-camera-camera-compose-documentation.md
new file mode 100644
index 0000000..bc5cbbc
--- /dev/null
+++ b/camera/camera-compose/src/main/java/androidx/camera/androidx-camera-camera-compose-documentation.md
@@ -0,0 +1,7 @@
+# Module root
+
+Camera Compose
+
+# Package androidx.camera.compose
+
+Jetpack Compose tools for users of the Jetpack Camera camera-core library
diff --git a/camera/camera-compose/src/main/java/androidx/camera/compose/CameraXViewfinder.kt b/camera/camera-compose/src/main/java/androidx/camera/compose/CameraXViewfinder.kt
new file mode 100644
index 0000000..cfc1f06
--- /dev/null
+++ b/camera/camera-compose/src/main/java/androidx/camera/compose/CameraXViewfinder.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2024 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.compose
+
+import androidx.camera.core.SurfaceRequest
+import androidx.camera.core.SurfaceRequest.TransformationInfo as CXTransformationInfo
+import androidx.camera.viewfinder.compose.MutableCoordinateTransformer
+import androidx.camera.viewfinder.compose.Viewfinder
+import androidx.camera.viewfinder.surface.ImplementationMode
+import androidx.camera.viewfinder.surface.TransformationInfo
+import androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.produceState
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Modifier
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.takeWhile
+import kotlinx.coroutines.launch
+
+/**
+ * An adapter composable that displays frames from CameraX by completing provided [SurfaceRequest]s.
+ *
+ * This is a wrapper around [Viewfinder] that will convert a CameraX [SurfaceRequest] internally
+ * into a [ViewfinderSurfaceRequest]. Additionally, all interactions normally handled through the
+ * [ViewfinderSurfaceRequest] will be derived from the [SurfaceRequest].
+ *
+ * If [implementationMode] is changed while the provided [surfaceRequest] has been fulfilled, the
+ * surface request will be invalidated as if [SurfaceRequest.invalidate] has been called. This will
+ * allow CameraX to know that a new surface request is required since the underlying viewfinder
+ * implementation will be providing a new surface.
+ *
+ * Example usage:
+ *
+ * @sample androidx.camera.compose.samples.CameraXViewfinderSample
+ * @param surfaceRequest The surface request from CameraX
+ * @param modifier The [Modifier] to be applied to this viewfinder
+ * @param implementationMode The [ImplementationMode] to be used by this viewfinder.
+ * @param coordinateTransformer The [MutableCoordinateTransformer] used to map offsets of this
+ *   viewfinder to the source coordinates of the data being provided to the surface that fulfills
+ *   [surfaceRequest]
+ */
+@Composable
+public fun CameraXViewfinder(
+    surfaceRequest: SurfaceRequest,
+    modifier: Modifier = Modifier,
+    implementationMode: ImplementationMode = ImplementationMode.EXTERNAL,
+    coordinateTransformer: MutableCoordinateTransformer? = null
+) {
+    val currentImplementationMode by rememberUpdatedState(implementationMode)
+
+    val viewfinderArgs by
+        produceState<ViewfinderArgs?>(initialValue = null, surfaceRequest) {
+            // Convert the CameraX SurfaceRequest to ViewfinderSurfaceRequest. There should
+            // always be a 1:1 mapping of CameraX SurfaceRequest to ViewfinderSurfaceRequest.
+            val viewfinderSurfaceRequest =
+                ViewfinderSurfaceRequest.Builder(surfaceRequest.resolution).build()
+
+            // Launch undispatched so we always reach the try/finally in this coroutine
+            launch(start = CoroutineStart.UNDISPATCHED) {
+                try {
+                    // Forward request cancellation to the ViewfinderSurfaceRequest by marking it
+                    // safe to release and cancelling this produceScope in case we haven't yet
+                    // produced a complete ViewfinderArgs.
+                    surfaceRequest.addRequestCancellationListener(Runnable::run) {
+                        // This SurfaceRequest doesn't need to be completed, so let the
+                        // Viewfinder know in case it has already generated a Surface.
+                        viewfinderSurfaceRequest.markSurfaceSafeToRelease()
+                        // Also complete the ViewfinderSurfaceRequest from the producer side
+                        // in case we never sent it to the Viewfinder.
+                        viewfinderSurfaceRequest.willNotProvideSurface()
+                        [email protected]()
+                    }
+
+                    // Suspend until we retrieve the Surface
+                    val surface = viewfinderSurfaceRequest.getSurface()
+                    // Provide the surface and mark safe to release once the
+                    // frame producer is finished.
+                    surfaceRequest.provideSurface(surface, Runnable::run) {
+                        viewfinderSurfaceRequest.markSurfaceSafeToRelease()
+                    }
+                } finally {
+                    // If we haven't provided the surface, such as if we're cancelled
+                    // while suspending on getSurface(), this call will succeed. Otherwise
+                    // it will be a no-op.
+                    surfaceRequest.willNotProvideSurface()
+                }
+            }
+
+            // Convert the CameraX TransformationInfo callback into a StateFlow
+            val transformationInfoFlow: StateFlow<CXTransformationInfo?> =
+                MutableStateFlow<CXTransformationInfo?>(null)
+                    .also { stateFlow ->
+                        // Set a callback to update this state flow
+                        surfaceRequest.setTransformationInfoListener(Runnable::run) { transformInfo
+                            ->
+                            // Set the next value of the flow
+                            stateFlow.value = transformInfo
+                        }
+                    }
+                    .asStateFlow()
+
+            // The ImplementationMode that will be used for all TransformationInfo updates.
+            // This is locked in once we have updated ViewfinderArgs and won't change until
+            // this produceState block is cancelled and restarted.
+            var snapshotImplementationMode: ImplementationMode? = null
+            snapshotFlow { currentImplementationMode }
+                .combine(transformationInfoFlow.filterNotNull()) { implMode, transformInfo ->
+                    Pair(implMode, transformInfo)
+                }
+                .takeWhile { (implMode, _) ->
+                    val shouldAbort =
+                        snapshotImplementationMode != null && implMode != snapshotImplementationMode
+                    if (shouldAbort) {
+                        // Abort flow and invalidate SurfaceRequest so a new SurfaceRequest will
+                        // be sent.
+                        surfaceRequest.invalidate()
+                    } else {
+                        // Got the first ImplementationMode. This will be used until this
+                        // produceState is cancelled.
+                        snapshotImplementationMode = implMode
+                    }
+                    !shouldAbort
+                }
+                .collect { (implMode, transformInfo) ->
+                    value =
+                        ViewfinderArgs(
+                            viewfinderSurfaceRequest,
+                            implMode,
+                            TransformationInfo(
+                                sourceRotation = transformInfo.rotationDegrees,
+                                isSourceMirroredHorizontally = transformInfo.isMirroring,
+                                isSourceMirroredVertically = false,
+                                cropRectLeft = transformInfo.cropRect.left,
+                                cropRectTop = transformInfo.cropRect.top,
+                                cropRectRight = transformInfo.cropRect.right,
+                                cropRectBottom = transformInfo.cropRect.bottom
+                            )
+                        )
+                }
+        }
+
+    viewfinderArgs?.let { args ->
+        Viewfinder(
+            surfaceRequest = args.viewfinderSurfaceRequest,
+            implementationMode = args.implementationMode,
+            transformationInfo = args.transformationInfo,
+            modifier = modifier.fillMaxSize(),
+            coordinateTransformer = coordinateTransformer
+        )
+    }
+}
+
+@Immutable
+private data class ViewfinderArgs(
+    val viewfinderSurfaceRequest: ViewfinderSurfaceRequest,
+    val implementationMode: ImplementationMode,
+    val transformationInfo: TransformationInfo
+)
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraProvider.kt b/camera/camera-core/src/main/java/androidx/camera/core/CameraProvider.kt
index 94fa6ae..a0557a5 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraProvider.kt
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraProvider.kt
@@ -18,6 +18,7 @@
 import androidx.annotation.RestrictTo
 import androidx.annotation.RestrictTo.Scope
 import androidx.lifecycle.LifecycleOwner
+import com.google.common.util.concurrent.ListenableFuture
 
 /**
  * A [CameraProvider] provides basic access to a set of cameras such as querying for camera
@@ -91,4 +92,12 @@
     public fun getCameraInfo(cameraSelector: CameraSelector): CameraInfo {
         throw UnsupportedOperationException("The camera provider is not implemented properly.")
     }
+
+    /**
+     * Shuts down the camera provider.
+     *
+     * @return A [ListenableFuture] representing the shutdown status. Cancellation of this future is
+     *   a no-op.
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP) public fun shutdownAsync(): ListenableFuture<Void>
 }
diff --git a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
index 0688972..2f44ee0 100644
--- a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
+++ b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
@@ -735,14 +735,6 @@
     }
 
     @Test
-    fun cannotConfigureTwice() {
-        ProcessCameraProvider.configureInstance(FakeAppConfig.create())
-        assertThrows<IllegalStateException> {
-            ProcessCameraProvider.configureInstance(FakeAppConfig.create())
-        }
-    }
-
-    @Test
     fun shutdown_clearsPreviousConfiguration() {
         ProcessCameraProvider.configureInstance(FakeAppConfig.create())
 
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraProvider.kt b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraProvider.kt
index 33cc057..08c1e72 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraProvider.kt
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraProvider.kt
@@ -15,11 +15,15 @@
  */
 package androidx.camera.lifecycle
 
+import android.content.Context
 import android.content.pm.PackageManager
+import androidx.annotation.RestrictTo
+import androidx.annotation.RestrictTo.Scope
 import androidx.camera.core.Camera
 import androidx.camera.core.CameraInfo
 import androidx.camera.core.CameraProvider
 import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraXConfig
 import androidx.camera.core.CompositionSettings
 import androidx.camera.core.ConcurrentCamera
 import androidx.camera.core.ConcurrentCamera.SingleCameraConfig
@@ -28,21 +32,27 @@
 import androidx.camera.core.Preview
 import androidx.camera.core.UseCase
 import androidx.camera.core.UseCaseGroup
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.core.impl.utils.futures.Futures
+import androidx.concurrent.futures.await
+import androidx.core.util.Preconditions
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
+import com.google.common.util.concurrent.ListenableFuture
 
 /**
- * Provides access to a camera which has has its opening and closing controlled by a
- * [LifecycleOwner].
+ * Provides access to a camera which has its opening and closing controlled by a [LifecycleOwner].
  */
-internal interface LifecycleCameraProvider : CameraProvider {
+// TODO: Remove the annotation when LifecycleCameraProvider is ready to be public.
+@RestrictTo(Scope.LIBRARY_GROUP)
+public interface LifecycleCameraProvider : CameraProvider {
     /**
      * Returns `true` if the [UseCase] is bound to a lifecycle. Otherwise returns `false`.
      *
      * After binding a use case, use cases remain bound until the lifecycle reaches a
      * [Lifecycle.State.DESTROYED] state or if is unbound by calls to [unbind] or [unbindAll].
      */
-    fun isBound(useCase: UseCase): Boolean
+    public fun isBound(useCase: UseCase): Boolean
 
     /**
      * Unbinds all specified use cases from the lifecycle provider.
@@ -57,8 +67,9 @@
      *
      * @param useCases The collection of use cases to remove.
      * @throws IllegalStateException If not called on main thread.
+     * @throws UnsupportedOperationException If called in concurrent mode.
      */
-    fun unbind(vararg useCases: UseCase?)
+    public fun unbind(vararg useCases: UseCase?): Unit
 
     /**
      * Unbinds all use cases from the lifecycle provider and removes them from CameraX.
@@ -67,7 +78,7 @@
      *
      * @throws IllegalStateException If not called on main thread.
      */
-    fun unbindAll()
+    public fun unbindAll(): Unit
 
     /**
      * Binds the collection of [UseCase] to a [LifecycleOwner].
@@ -124,7 +135,7 @@
      *   camera to be used for the given use cases.
      * @throws UnsupportedOperationException If the camera is configured in concurrent mode.
      */
-    fun bindToLifecycle(
+    public fun bindToLifecycle(
         lifecycleOwner: LifecycleOwner,
         cameraSelector: CameraSelector,
         vararg useCases: UseCase?
@@ -142,7 +153,7 @@
      *
      * @throws UnsupportedOperationException If the camera is configured in concurrent mode.
      */
-    fun bindToLifecycle(
+    public fun bindToLifecycle(
         lifecycleOwner: LifecycleOwner,
         cameraSelector: CameraSelector,
         useCaseGroup: UseCaseGroup
@@ -169,8 +180,9 @@
      *
      * If the concurrent logical cameras are binding the same preview and video capture use cases,
      * the concurrent cameras video recording will be supported. The concurrent camera preview
-     * stream will be shared with video capture and record the concurrent cameras as a whole. The
-     * [CompositionSettings] can be used to configure the position of each camera stream.
+     * stream will be shared with video capture and record the concurrent cameras streams as a
+     * composited stream. The [CompositionSettings] can be used to configure the position of each
+     * camera stream and different layouts can be built. See [CompositionSettings] for more details.
      *
      * If we want to open concurrent physical cameras, which are two front cameras or two back
      * cameras, the device needs to support physical cameras and the capability could be checked via
@@ -206,5 +218,48 @@
      * @see CameraInfo.isLogicalMultiCameraSupported
      * @see CameraInfo.getPhysicalCameraInfos
      */
-    fun bindToLifecycle(singleCameraConfigs: List<SingleCameraConfig?>): ConcurrentCamera
+    public fun bindToLifecycle(singleCameraConfigs: List<SingleCameraConfig?>): ConcurrentCamera
+
+    public companion object {
+        /**
+         * Creates a lifecycle camera provider instance.
+         *
+         * @param context The Application context.
+         * @param cameraXConfig The configuration options to configure the lifecycle camera
+         *   provider. If not set, the default configuration will be used.
+         * @return The lifecycle camera provider instance.
+         */
+        @JvmOverloads
+        @JvmStatic
+        public suspend fun createInstance(
+            context: Context,
+            cameraXConfig: CameraXConfig? = null,
+        ): LifecycleCameraProvider {
+            return createInstanceAsync(context, cameraXConfig).await()
+        }
+
+        /**
+         * Creates a lifecycle camera provider instance asynchronously.
+         *
+         * @param context The Application context.
+         * @param cameraXConfig The configuration options to configure the lifecycle camera
+         *   provider. If not set, the default configuration will be used.
+         * @return A [ListenableFuture] that will be completed when the lifecycle camera provider
+         *   instance is initialized.
+         */
+        @JvmOverloads
+        @JvmStatic
+        public fun createInstanceAsync(
+            context: Context,
+            cameraXConfig: CameraXConfig? = null,
+        ): ListenableFuture<LifecycleCameraProvider> {
+            Preconditions.checkNotNull(context)
+            val lifecycleCameraProvider = LifecycleCameraProviderImpl()
+            return Futures.transform(
+                lifecycleCameraProvider.initAsync(context, cameraXConfig),
+                { lifecycleCameraProvider },
+                CameraXExecutors.directExecutor()
+            )
+        }
+    }
 }
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraProviderImpl.kt b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraProviderImpl.kt
new file mode 100644
index 0000000..bb2d4f4
--- /dev/null
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraProviderImpl.kt
@@ -0,0 +1,711 @@
+/*
+ * Copyright 2024 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.lifecycle
+
+import android.content.Context
+import android.content.pm.PackageManager.FEATURE_CAMERA_CONCURRENT
+import androidx.annotation.GuardedBy
+import androidx.annotation.MainThread
+import androidx.camera.core.Camera
+import androidx.camera.core.CameraEffect
+import androidx.camera.core.CameraFilter
+import androidx.camera.core.CameraInfo
+import androidx.camera.core.CameraInfoUnavailableException
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraX
+import androidx.camera.core.CameraXConfig
+import androidx.camera.core.CompositionSettings
+import androidx.camera.core.ConcurrentCamera
+import androidx.camera.core.ConcurrentCamera.SingleCameraConfig
+import androidx.camera.core.ImageAnalysis
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.Preview
+import androidx.camera.core.UseCase
+import androidx.camera.core.UseCaseGroup
+import androidx.camera.core.ViewPort
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_SINGLE
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
+import androidx.camera.core.concurrent.CameraCoordinator.CameraOperatingMode
+import androidx.camera.core.impl.CameraConfig
+import androidx.camera.core.impl.CameraConfigs
+import androidx.camera.core.impl.CameraInternal
+import androidx.camera.core.impl.ExtendedCameraConfigProviderStore
+import androidx.camera.core.impl.RestrictedCameraInfo
+import androidx.camera.core.impl.UseCaseConfig
+import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
+import androidx.camera.core.impl.utils.ContextUtil
+import androidx.camera.core.impl.utils.Threads
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.core.impl.utils.futures.FutureCallback
+import androidx.camera.core.impl.utils.futures.FutureChain
+import androidx.camera.core.impl.utils.futures.Futures
+import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.concurrent.futures.CallbackToFutureAdapter
+import androidx.core.util.Preconditions
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.tracing.trace
+import com.google.common.util.concurrent.ListenableFuture
+import java.util.Objects
+import java.util.Objects.requireNonNull
+
+/** Implementation of the [LifecycleCameraProvider] interface. */
+internal class LifecycleCameraProviderImpl : LifecycleCameraProvider {
+    private val lock = Any()
+    @GuardedBy("mLock") private var cameraXConfigProvider: CameraXConfig.Provider? = null
+    @GuardedBy("mLock") private var cameraXInitializeFuture: ListenableFuture<Void>? = null
+    @GuardedBy("mLock") private var cameraXShutdownFuture = Futures.immediateFuture<Void>(null)
+    private val lifecycleCameraRepository = LifecycleCameraRepository()
+    private var cameraX: CameraX? = null
+    private var context: Context? = null
+    @GuardedBy("mLock")
+    private val cameraInfoMap: MutableMap<CameraUseCaseAdapter.CameraId, RestrictedCameraInfo> =
+        HashMap()
+
+    internal fun initAsync(
+        context: Context,
+        cameraXConfig: CameraXConfig? = null
+    ): ListenableFuture<Void> {
+        synchronized(lock) {
+            if (cameraXInitializeFuture != null) {
+                return cameraXInitializeFuture as ListenableFuture<Void>
+            }
+            cameraXConfig?.let { configure(it) }
+            val cameraX = CameraX(context, cameraXConfigProvider)
+
+            cameraXInitializeFuture =
+                CallbackToFutureAdapter.getFuture { completer ->
+                    synchronized(lock) {
+                        val future: ListenableFuture<Void> =
+                            FutureChain.from(cameraXShutdownFuture)
+                                .transformAsync(
+                                    { cameraX.initializeFuture },
+                                    CameraXExecutors.directExecutor()
+                                )
+                        Futures.addCallback(
+                            future,
+                            object : FutureCallback<Void?> {
+                                override fun onSuccess(result: Void?) {
+                                    [email protected] = cameraX
+                                    [email protected] =
+                                        ContextUtil.getApplicationContext(context)
+                                    completer.set(null)
+                                }
+
+                                override fun onFailure(t: Throwable) {
+                                    completer.setException(t)
+                                }
+                            },
+                            CameraXExecutors.directExecutor()
+                        )
+                    }
+
+                    "LifecycleCameraProvider-initialize"
+                }
+
+            return cameraXInitializeFuture as ListenableFuture<Void>
+        }
+    }
+
+    /**
+     * Configures the camera provider.
+     *
+     * The camera provider can only be configured once. Trying to configure it multiple times will
+     * throw an [IllegalStateException].
+     *
+     * @param cameraXConfig The CameraX configuration.
+     */
+    internal fun configure(cameraXConfig: CameraXConfig) =
+        trace("CX:configureInstanceInternal") {
+            synchronized(lock) {
+                Preconditions.checkNotNull(cameraXConfig)
+                Preconditions.checkState(
+                    cameraXConfigProvider == null,
+                    "CameraX has already been configured. To use a different configuration, " +
+                        "shutdown() must be called."
+                )
+                cameraXConfigProvider = CameraXConfig.Provider { cameraXConfig }
+            }
+        }
+
+    override fun shutdownAsync(): ListenableFuture<Void> {
+        Threads.runOnMainSync {
+            unbindAll()
+            lifecycleCameraRepository.clear()
+        }
+
+        if (cameraX != null) {
+            cameraX!!.cameraFactory.cameraCoordinator.shutdown()
+        }
+
+        val shutdownFuture =
+            if (cameraX != null) cameraX!!.shutdown() else Futures.immediateFuture<Void>(null)
+
+        synchronized(lock) {
+            cameraXConfigProvider = null
+            cameraXInitializeFuture = null
+            cameraXShutdownFuture = shutdownFuture
+            cameraInfoMap.clear()
+        }
+        cameraX = null
+        context = null
+        return shutdownFuture
+    }
+
+    override fun isBound(useCase: UseCase): Boolean {
+        for (lifecycleCamera: LifecycleCamera in lifecycleCameraRepository.lifecycleCameras) {
+            if (lifecycleCamera.isBound(useCase)) {
+                return true
+            }
+        }
+
+        return false
+    }
+
+    @MainThread
+    override fun unbind(vararg useCases: UseCase?): Unit =
+        trace("CX:unbind") {
+            Threads.checkMainThread()
+
+            if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) {
+                throw UnsupportedOperationException(
+                    "Unbind usecase is not supported in concurrent camera mode, call unbindAll() first."
+                )
+            }
+
+            lifecycleCameraRepository.unbind(listOf(*useCases))
+        }
+
+    @MainThread
+    override fun unbindAll(): Unit =
+        trace("CX:unbindAll") {
+            Threads.checkMainThread()
+            cameraOperatingMode = CAMERA_OPERATING_MODE_UNSPECIFIED
+            lifecycleCameraRepository.unbindAll()
+        }
+
+    @Throws(CameraInfoUnavailableException::class)
+    override fun hasCamera(cameraSelector: CameraSelector): Boolean =
+        trace("CX:hasCamera") {
+            try {
+                cameraSelector.select(cameraX!!.cameraRepository.cameras)
+            } catch (e: IllegalArgumentException) {
+                return@trace false
+            }
+
+            return@trace true
+        }
+
+    @MainThread
+    override fun bindToLifecycle(
+        lifecycleOwner: LifecycleOwner,
+        cameraSelector: CameraSelector,
+        vararg useCases: UseCase?
+    ): Camera =
+        trace("CX:bindToLifecycle") {
+            if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) {
+                throw UnsupportedOperationException(
+                    "bindToLifecycle for single camera is not supported in concurrent camera mode, " +
+                        "call unbindAll() first"
+                )
+            }
+            cameraOperatingMode = CAMERA_OPERATING_MODE_SINGLE
+            val camera =
+                bindToLifecycle(
+                    lifecycleOwner,
+                    cameraSelector,
+                    null,
+                    CompositionSettings.DEFAULT,
+                    CompositionSettings.DEFAULT,
+                    null,
+                    emptyList<CameraEffect>(),
+                    *useCases
+                )
+            return@trace camera
+        }
+
+    @MainThread
+    public override fun bindToLifecycle(
+        lifecycleOwner: LifecycleOwner,
+        cameraSelector: CameraSelector,
+        useCaseGroup: UseCaseGroup
+    ): Camera =
+        trace("CX:bindToLifecycle-UseCaseGroup") {
+            if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) {
+                throw UnsupportedOperationException(
+                    "bindToLifecycle for single camera is not supported in concurrent camera mode, " +
+                        "call unbindAll() first."
+                )
+            }
+            cameraOperatingMode = CAMERA_OPERATING_MODE_SINGLE
+            val camera =
+                bindToLifecycle(
+                    lifecycleOwner,
+                    cameraSelector,
+                    null,
+                    CompositionSettings.DEFAULT,
+                    CompositionSettings.DEFAULT,
+                    useCaseGroup.viewPort,
+                    useCaseGroup.effects,
+                    *useCaseGroup.useCases.toTypedArray<UseCase>()
+                )
+            return@trace camera
+        }
+
+    @MainThread
+    override fun bindToLifecycle(singleCameraConfigs: List<SingleCameraConfig?>): ConcurrentCamera =
+        trace("CX:bindToLifecycle-Concurrent") {
+            if (singleCameraConfigs.size < 2) {
+                throw IllegalArgumentException("Concurrent camera needs two camera configs.")
+            }
+
+            if (singleCameraConfigs.size > 2) {
+                throw IllegalArgumentException(
+                    "Concurrent camera is only supporting two cameras at maximum."
+                )
+            }
+
+            val firstCameraConfig = singleCameraConfigs[0]!!
+            val secondCameraConfig = singleCameraConfigs[1]!!
+
+            val cameras: MutableList<Camera> = ArrayList()
+            if (
+                firstCameraConfig.cameraSelector.lensFacing ==
+                    secondCameraConfig.cameraSelector.lensFacing
+            ) {
+                if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) {
+                    throw UnsupportedOperationException(
+                        "Camera is already running, call unbindAll() before binding more cameras."
+                    )
+                }
+                if (
+                    firstCameraConfig.lifecycleOwner != secondCameraConfig.lifecycleOwner ||
+                        firstCameraConfig.useCaseGroup.viewPort !=
+                            secondCameraConfig.useCaseGroup.viewPort ||
+                        firstCameraConfig.useCaseGroup.effects !=
+                            secondCameraConfig.useCaseGroup.effects
+                ) {
+                    throw IllegalArgumentException(
+                        "Two camera configs need to have the same lifecycle owner, view port and " +
+                            "effects."
+                    )
+                }
+                val lifecycleOwner = firstCameraConfig.lifecycleOwner
+                val cameraSelector = firstCameraConfig.cameraSelector
+                val viewPort = firstCameraConfig.useCaseGroup.viewPort
+                val effects = firstCameraConfig.useCaseGroup.effects
+                val useCases: MutableList<UseCase> = ArrayList()
+                for (config: SingleCameraConfig? in singleCameraConfigs) {
+                    // Connect physical camera id with use case.
+                    for (useCase: UseCase in config!!.useCaseGroup.useCases) {
+                        config.cameraSelector.physicalCameraId?.let {
+                            useCase.setPhysicalCameraId(it)
+                        }
+                    }
+                    useCases.addAll(config.useCaseGroup.useCases)
+                }
+
+                cameraOperatingMode = CAMERA_OPERATING_MODE_SINGLE
+                val camera =
+                    bindToLifecycle(
+                        lifecycleOwner,
+                        cameraSelector,
+                        null,
+                        CompositionSettings.DEFAULT,
+                        CompositionSettings.DEFAULT,
+                        viewPort,
+                        effects,
+                        *useCases.toTypedArray<UseCase>()
+                    )
+                cameras.add(camera)
+            } else {
+                if (!context!!.packageManager.hasSystemFeature(FEATURE_CAMERA_CONCURRENT)) {
+                    throw UnsupportedOperationException(
+                        "Concurrent camera is not supported on the device."
+                    )
+                }
+
+                if (cameraOperatingMode == CAMERA_OPERATING_MODE_SINGLE) {
+                    throw UnsupportedOperationException(
+                        "Camera is already running, call unbindAll() before binding more cameras."
+                    )
+                }
+
+                val cameraInfosToBind: MutableList<CameraInfo> = ArrayList()
+                val firstCameraInfo: CameraInfo
+                val secondCameraInfo: CameraInfo
+                try {
+                    firstCameraInfo = getCameraInfo(firstCameraConfig.cameraSelector)
+                    secondCameraInfo = getCameraInfo(secondCameraConfig.cameraSelector)
+                } catch (e: IllegalArgumentException) {
+                    throw IllegalArgumentException("Invalid camera selectors in camera configs.")
+                }
+                cameraInfosToBind.add(firstCameraInfo)
+                cameraInfosToBind.add(secondCameraInfo)
+                if (
+                    activeConcurrentCameraInfos.isNotEmpty() &&
+                        cameraInfosToBind != activeConcurrentCameraInfos
+                ) {
+                    throw UnsupportedOperationException(
+                        "Cameras are already running, call unbindAll() before binding more cameras."
+                    )
+                }
+
+                cameraOperatingMode = CAMERA_OPERATING_MODE_CONCURRENT
+
+                // For dual camera video capture, we are only supporting two use cases:
+                // Preview + VideoCapture. If ImageCapture support is added, the validation logic
+                // will be updated accordingly.
+                var isDualCameraVideoCapture = false
+                if (
+                    Objects.equals(
+                        firstCameraConfig.useCaseGroup.useCases,
+                        secondCameraConfig.useCaseGroup.useCases
+                    ) && firstCameraConfig.useCaseGroup.useCases.size == 2
+                ) {
+                    val useCase0 = firstCameraConfig.useCaseGroup.useCases[0]
+                    val useCase1 = firstCameraConfig.useCaseGroup.useCases[1]
+                    isDualCameraVideoCapture =
+                        (isVideoCapture(useCase0) && isPreview(useCase1)) ||
+                            (isPreview(useCase0) && isVideoCapture(useCase1))
+                }
+
+                if (isDualCameraVideoCapture) {
+                    cameras.add(
+                        bindToLifecycle(
+                            firstCameraConfig.lifecycleOwner,
+                            firstCameraConfig.cameraSelector,
+                            secondCameraConfig.cameraSelector,
+                            firstCameraConfig.compositionSettings,
+                            secondCameraConfig.compositionSettings,
+                            firstCameraConfig.useCaseGroup.viewPort,
+                            firstCameraConfig.useCaseGroup.effects,
+                            *firstCameraConfig.useCaseGroup.useCases.toTypedArray<UseCase>(),
+                        )
+                    )
+                } else {
+                    for (config: SingleCameraConfig? in singleCameraConfigs) {
+                        val camera =
+                            bindToLifecycle(
+                                config!!.lifecycleOwner,
+                                config.cameraSelector,
+                                null,
+                                CompositionSettings.DEFAULT,
+                                CompositionSettings.DEFAULT,
+                                config.useCaseGroup.viewPort,
+                                config.useCaseGroup.effects,
+                                *config.useCaseGroup.useCases.toTypedArray<UseCase>()
+                            )
+                        cameras.add(camera)
+                    }
+                }
+                activeConcurrentCameraInfos = cameraInfosToBind
+            }
+            return@trace ConcurrentCamera(cameras)
+        }
+
+    override val availableCameraInfos: List<CameraInfo>
+        get() =
+            trace("CX:getAvailableCameraInfos") {
+                val availableCameraInfos: MutableList<CameraInfo> = ArrayList()
+                val cameras: Set<CameraInternal> = cameraX!!.cameraRepository.cameras
+                for (camera: CameraInternal in cameras) {
+                    availableCameraInfos.add(camera.cameraInfo)
+                }
+                return@trace availableCameraInfos
+            }
+
+    override val availableConcurrentCameraInfos: List<List<CameraInfo>>
+        get() =
+            trace("CX:getAvailableConcurrentCameraInfos") {
+                requireNonNull(cameraX)
+                requireNonNull(cameraX!!.cameraFactory.cameraCoordinator)
+                val concurrentCameraSelectorLists =
+                    cameraX!!.cameraFactory.cameraCoordinator.concurrentCameraSelectors
+
+                val availableConcurrentCameraInfos: MutableList<List<CameraInfo>> = ArrayList()
+                for (cameraSelectors in concurrentCameraSelectorLists) {
+                    val cameraInfos: MutableList<CameraInfo> = ArrayList()
+                    for (cameraSelector in cameraSelectors) {
+                        var cameraInfo: CameraInfo
+                        try {
+                            cameraInfo = getCameraInfo(cameraSelector)
+                        } catch (e: IllegalArgumentException) {
+                            continue
+                        }
+                        cameraInfos.add(cameraInfo)
+                    }
+                    availableConcurrentCameraInfos.add(cameraInfos)
+                }
+                return@trace availableConcurrentCameraInfos
+            }
+
+    override val isConcurrentCameraModeOn: Boolean
+        /**
+         * Returns whether there is a [ConcurrentCamera] bound.
+         *
+         * @return `true` if there is a [ConcurrentCamera] bound, otherwise `false`.
+         */
+        @MainThread get() = cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT
+
+    /**
+     * Binds [ViewPort] and a collection of [UseCase] to a [LifecycleOwner].
+     *
+     * The state of the lifecycle will determine when the cameras are open, started, stopped and
+     * closed. When started, the use cases receive camera data.
+     *
+     * Binding to a [LifecycleOwner] in state currently in [Lifecycle.State.STARTED] or greater will
+     * also initialize and start data capture. If the camera was already running this may cause a
+     * new initialization to occur temporarily stopping data from the camera before restarting it.
+     *
+     * Multiple use cases can be bound via adding them all to a single [bindToLifecycle] call, or by
+     * using multiple [bindToLifecycle] calls. Using a single call that includes all the use cases
+     * helps to set up a camera session correctly for all uses cases, such as by allowing
+     * determination of resolutions depending on all the use cases bound being bound. If the use
+     * cases are bound separately, it will find the supported resolution with the priority depending
+     * on the binding sequence. If the use cases are bound with a single call, it will find the
+     * supported resolution with the priority in sequence of [ImageCapture], [Preview] and then
+     * [ImageAnalysis]. The resolutions that can be supported depends on the camera device hardware
+     * level that there are some default guaranteed resolutions listed in
+     * [android.hardware.camera2.CameraDevice.createCaptureSession].
+     *
+     * Currently up to 3 use cases may be bound to a [Lifecycle] at any time. Exceeding capability
+     * of target camera device will throw an IllegalArgumentException.
+     *
+     * A [UseCase] should only be bound to a single lifecycle and camera selector a time. Attempting
+     * to bind a use case to a lifecycle when it is already bound to another lifecycle is an error,
+     * and the use case binding will not change. Attempting to bind the same use case to multiple
+     * camera selectors is also an error and will not change the binding.
+     *
+     * If different use cases are bound to different camera selectors that resolve to distinct
+     * cameras, but the same lifecycle, only one of the cameras will operate at a time. The
+     * non-operating camera will not become active until it is the only camera with use cases bound.
+     *
+     * The [Camera] returned is determined by the given camera selector, plus other internal
+     * requirements, possibly from use case configurations. The camera returned from
+     * [bindToLifecycle] may differ from the camera determined solely by a camera selector. If the
+     * camera selector can't resolve a camera under the requirements, an [IllegalArgumentException]
+     * will be thrown.
+     *
+     * Only [UseCase] bound to latest active [Lifecycle] can keep alive. [UseCase] bound to other
+     * [Lifecycle] will be stopped.
+     *
+     * @param lifecycleOwner The [LifecycleOwner] which controls the lifecycle transitions of the
+     *   use cases.
+     * @param primaryCameraSelector The primary camera selector which determines the camera to use
+     *   for set of use cases.
+     * @param secondaryCameraSelector The secondary camera selector in dual camera case.
+     * @param primaryCompositionSettings The composition settings for the primary camera.
+     * @param secondaryCompositionSettings The composition settings for the secondary camera.
+     * @param viewPort The viewPort which represents the visible camera sensor rect.
+     * @param effects The effects applied to the camera outputs.
+     * @param useCases The use cases to bind to a lifecycle.
+     * @return The [Camera] instance which is determined by the camera selector and internal
+     *   requirements.
+     * @throws IllegalStateException If the use case has already been bound to another lifecycle or
+     *   method is not called on main thread.
+     * @throws IllegalArgumentException If the provided camera selector is unable to resolve a
+     *   camera to be used for the given use cases.
+     */
+    @Suppress("unused")
+    private fun bindToLifecycle(
+        lifecycleOwner: LifecycleOwner,
+        primaryCameraSelector: CameraSelector,
+        secondaryCameraSelector: CameraSelector?,
+        primaryCompositionSettings: CompositionSettings,
+        secondaryCompositionSettings: CompositionSettings,
+        viewPort: ViewPort?,
+        effects: List<CameraEffect?>,
+        vararg useCases: UseCase?
+    ): Camera =
+        trace("CX:bindToLifecycle-internal") {
+            Threads.checkMainThread()
+            // TODO(b/153096869): override UseCase's target rotation.
+
+            // Get the LifecycleCamera if existed.
+            val primaryCameraInternal =
+                primaryCameraSelector.select(cameraX!!.cameraRepository.cameras)
+            primaryCameraInternal.setPrimary(true)
+            val primaryRestrictedCameraInfo =
+                getCameraInfo(primaryCameraSelector) as RestrictedCameraInfo
+
+            var secondaryCameraInternal: CameraInternal? = null
+            var secondaryRestrictedCameraInfo: RestrictedCameraInfo? = null
+            if (secondaryCameraSelector != null) {
+                secondaryCameraInternal =
+                    secondaryCameraSelector.select(cameraX!!.cameraRepository.cameras)
+                secondaryCameraInternal.setPrimary(false)
+                secondaryRestrictedCameraInfo =
+                    getCameraInfo(secondaryCameraSelector) as RestrictedCameraInfo
+            }
+
+            var lifecycleCameraToBind =
+                lifecycleCameraRepository.getLifecycleCamera(
+                    lifecycleOwner,
+                    CameraUseCaseAdapter.generateCameraId(
+                        primaryRestrictedCameraInfo,
+                        secondaryRestrictedCameraInfo
+                    )
+                )
+
+            // Check if there's another camera that has already been bound.
+            val lifecycleCameras = lifecycleCameraRepository.lifecycleCameras
+            useCases.filterNotNull().forEach { useCase ->
+                for (lifecycleCamera: LifecycleCamera in lifecycleCameras) {
+                    if (
+                        lifecycleCamera.isBound(useCase) && lifecycleCamera != lifecycleCameraToBind
+                    ) {
+                        throw IllegalStateException(
+                            String.format(
+                                "Use case %s already bound to a different lifecycle.",
+                                useCase
+                            )
+                        )
+                    }
+                }
+            }
+
+            // Create the LifecycleCamera if there's no existing one that can be used.
+            if (lifecycleCameraToBind == null) {
+                lifecycleCameraToBind =
+                    lifecycleCameraRepository.createLifecycleCamera(
+                        lifecycleOwner,
+                        CameraUseCaseAdapter(
+                            primaryCameraInternal,
+                            secondaryCameraInternal,
+                            primaryRestrictedCameraInfo,
+                            secondaryRestrictedCameraInfo,
+                            primaryCompositionSettings,
+                            secondaryCompositionSettings,
+                            cameraX!!.cameraFactory.cameraCoordinator,
+                            cameraX!!.cameraDeviceSurfaceManager,
+                            cameraX!!.defaultConfigFactory
+                        )
+                    )
+            }
+
+            if (useCases.isEmpty()) {
+                return@trace lifecycleCameraToBind!!
+            }
+
+            lifecycleCameraRepository.bindToLifecycleCamera(
+                lifecycleCameraToBind!!,
+                viewPort,
+                effects,
+                listOf(*useCases),
+                cameraX!!.cameraFactory.cameraCoordinator
+            )
+
+            return@trace lifecycleCameraToBind
+        }
+
+    override fun getCameraInfo(cameraSelector: CameraSelector): CameraInfo =
+        trace("CX:getCameraInfo") {
+            val cameraInfoInternal =
+                cameraSelector.select(cameraX!!.cameraRepository.cameras).cameraInfoInternal
+            val cameraConfig = getCameraConfig(cameraSelector, cameraInfoInternal)
+
+            val key =
+                CameraUseCaseAdapter.CameraId.create(
+                    cameraInfoInternal.cameraId,
+                    cameraConfig.compatibilityId
+                )
+            var restrictedCameraInfo: RestrictedCameraInfo?
+            synchronized(lock) {
+                restrictedCameraInfo = cameraInfoMap[key]
+                if (restrictedCameraInfo == null) {
+                    restrictedCameraInfo = RestrictedCameraInfo(cameraInfoInternal, cameraConfig)
+                    cameraInfoMap[key] = restrictedCameraInfo!!
+                }
+            }
+
+            return@trace restrictedCameraInfo!!
+        }
+
+    private fun isVideoCapture(useCase: UseCase): Boolean {
+        return useCase.currentConfig.containsOption(UseCaseConfig.OPTION_CAPTURE_TYPE) &&
+            useCase.currentConfig.captureType == CaptureType.VIDEO_CAPTURE
+    }
+
+    private fun isPreview(useCase: UseCase): Boolean {
+        return useCase is Preview
+    }
+
+    private fun getCameraConfig(
+        cameraSelector: CameraSelector,
+        cameraInfo: CameraInfo
+    ): CameraConfig {
+        var cameraConfig: CameraConfig? = null
+        for (cameraFilter: CameraFilter in cameraSelector.cameraFilterSet) {
+            if (cameraFilter.identifier != CameraFilter.DEFAULT_ID) {
+                val extendedCameraConfig =
+                    ExtendedCameraConfigProviderStore.getConfigProvider(cameraFilter.identifier)
+                        .getConfig(cameraInfo, (context)!!)
+                if (extendedCameraConfig == null) { // ignore IDs unrelated to camera configs.
+                    continue
+                }
+
+                // Only allows one camera config now.
+                if (cameraConfig != null) {
+                    throw IllegalArgumentException(
+                        "Cannot apply multiple extended camera configs at the same time."
+                    )
+                }
+                cameraConfig = extendedCameraConfig
+            }
+        }
+
+        if (cameraConfig == null) {
+            cameraConfig = CameraConfigs.defaultConfig()
+        }
+        return cameraConfig
+    }
+
+    @get:CameraOperatingMode
+    private var cameraOperatingMode: Int
+        get() {
+            if (cameraX == null) {
+                return CAMERA_OPERATING_MODE_UNSPECIFIED
+            }
+            return cameraX!!.cameraFactory.cameraCoordinator.cameraOperatingMode
+        }
+        private set(cameraOperatingMode) {
+            if (cameraX == null) {
+                return
+            }
+            cameraX!!.cameraFactory.cameraCoordinator.cameraOperatingMode = cameraOperatingMode
+        }
+
+    private var activeConcurrentCameraInfos: List<CameraInfo>
+        get() {
+            if (cameraX == null) {
+                return java.util.ArrayList()
+            }
+            return cameraX!!.cameraFactory.cameraCoordinator.activeConcurrentCameraInfos
+        }
+        private set(cameraInfos) {
+            if (cameraX == null) {
+                return
+            }
+            cameraX!!.cameraFactory.cameraCoordinator.activeConcurrentCameraInfos = cameraInfos
+        }
+
+    companion object {
+        private const val TAG = "LifecycleCameraProvider"
+    }
+}
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.kt b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.kt
index ca49e20..9814e90 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.kt
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.kt
@@ -18,55 +18,24 @@
 
 import android.app.Application
 import android.content.Context
-import android.content.pm.PackageManager.FEATURE_CAMERA_CONCURRENT
-import androidx.annotation.GuardedBy
 import androidx.annotation.MainThread
 import androidx.annotation.VisibleForTesting
 import androidx.camera.core.Camera
-import androidx.camera.core.CameraEffect
-import androidx.camera.core.CameraFilter
 import androidx.camera.core.CameraInfo
 import androidx.camera.core.CameraInfoUnavailableException
 import androidx.camera.core.CameraSelector
-import androidx.camera.core.CameraX
 import androidx.camera.core.CameraXConfig
-import androidx.camera.core.CompositionSettings
 import androidx.camera.core.ConcurrentCamera
-import androidx.camera.core.ConcurrentCamera.SingleCameraConfig
-import androidx.camera.core.ImageAnalysis
-import androidx.camera.core.ImageCapture
 import androidx.camera.core.InitializationException
-import androidx.camera.core.Preview
 import androidx.camera.core.UseCase
 import androidx.camera.core.UseCaseGroup
-import androidx.camera.core.ViewPort
-import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT
-import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_SINGLE
-import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
-import androidx.camera.core.concurrent.CameraCoordinator.CameraOperatingMode
-import androidx.camera.core.impl.CameraConfig
-import androidx.camera.core.impl.CameraConfigs
-import androidx.camera.core.impl.CameraInternal
-import androidx.camera.core.impl.ExtendedCameraConfigProviderStore
-import androidx.camera.core.impl.RestrictedCameraInfo
-import androidx.camera.core.impl.UseCaseConfig
-import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
-import androidx.camera.core.impl.utils.ContextUtil
-import androidx.camera.core.impl.utils.Threads
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
-import androidx.camera.core.impl.utils.futures.FutureCallback
-import androidx.camera.core.impl.utils.futures.FutureChain
 import androidx.camera.core.impl.utils.futures.Futures
-import androidx.camera.core.internal.CameraUseCaseAdapter
 import androidx.camera.lifecycle.ProcessCameraProvider.Companion.getInstance
-import androidx.concurrent.futures.CallbackToFutureAdapter
 import androidx.core.util.Preconditions
-import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
 import androidx.tracing.trace
 import com.google.common.util.concurrent.ListenableFuture
-import java.util.Objects
-import java.util.Objects.requireNonNull
 
 /**
  * A singleton which can be used to bind the lifecycle of cameras to any [LifecycleOwner] within an
@@ -82,743 +51,85 @@
  *
  * This is the standard provider for applications to use.
  */
-public class ProcessCameraProvider private constructor() : LifecycleCameraProvider {
-    private val mLock = Any()
+// TODO: Remove the annotation when LifecycleCameraProvider is ready to be public.
+@Suppress("HiddenSuperclass")
+public class ProcessCameraProvider
+private constructor(private val lifecycleCameraProvider: LifecycleCameraProviderImpl) :
+    LifecycleCameraProvider {
 
-    @GuardedBy("mLock") private var mCameraXConfigProvider: CameraXConfig.Provider? = null
-
-    @GuardedBy("mLock") private var mCameraXInitializeFuture: ListenableFuture<CameraX>? = null
-
-    @GuardedBy("mLock") private var mCameraXShutdownFuture = Futures.immediateFuture<Void>(null)
-
-    private val mLifecycleCameraRepository = LifecycleCameraRepository()
-    private var mCameraX: CameraX? = null
-    private var mContext: Context? = null
-
-    @GuardedBy("mLock")
-    private val mCameraInfoMap: MutableMap<CameraUseCaseAdapter.CameraId, RestrictedCameraInfo> =
-        HashMap()
-
-    /**
-     * Allows shutting down this ProcessCameraProvider instance so a new instance can be retrieved
-     * by [getInstance].
-     *
-     * Once shutdownAsync is invoked, a new instance can be retrieved with [getInstance].
-     *
-     * This method should be used for testing purposes only. Along with [configureInstance], this
-     * allows the process camera provider to be used in test suites which may need to initialize
-     * CameraX in different ways in between tests.
-     *
-     * @return A [ListenableFuture] representing the shutdown status. Cancellation of this future is
-     *   a no-op.
-     */
-    @VisibleForTesting
-    public fun shutdownAsync(): ListenableFuture<Void> {
-        Threads.runOnMainSync {
-            unbindAll()
-            mLifecycleCameraRepository.clear()
-        }
-
-        if (mCameraX != null) {
-            mCameraX!!.cameraFactory.cameraCoordinator.shutdown()
-        }
-
-        val shutdownFuture =
-            if (mCameraX != null) mCameraX!!.shutdown() else Futures.immediateFuture<Void>(null)
-
-        synchronized(mLock) {
-            mCameraXConfigProvider = null
-            mCameraXInitializeFuture = null
-            mCameraXShutdownFuture = shutdownFuture
-            mCameraInfoMap.clear()
-        }
-        mCameraX = null
-        mContext = null
-        return shutdownFuture
+    override fun isBound(useCase: UseCase): Boolean {
+        return lifecycleCameraProvider.isBound(useCase)
     }
 
     @MainThread
-    public override fun bindToLifecycle(
+    override fun unbind(vararg useCases: UseCase?) {
+        return lifecycleCameraProvider.unbind(*useCases)
+    }
+
+    @MainThread
+    override fun unbindAll() {
+        return lifecycleCameraProvider.unbindAll()
+    }
+
+    @MainThread
+    override fun bindToLifecycle(
         lifecycleOwner: LifecycleOwner,
         cameraSelector: CameraSelector,
         vararg useCases: UseCase?
-    ): Camera =
-        trace("CX:bindToLifecycle") {
-            if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) {
-                throw UnsupportedOperationException(
-                    "bindToLifecycle for single camera is not supported in concurrent camera mode, " +
-                        "call unbindAll() first"
-                )
-            }
-            cameraOperatingMode = CAMERA_OPERATING_MODE_SINGLE
-            val camera =
-                bindToLifecycle(
-                    lifecycleOwner,
-                    cameraSelector,
-                    null,
-                    CompositionSettings.DEFAULT,
-                    CompositionSettings.DEFAULT,
-                    null,
-                    emptyList<CameraEffect>(),
-                    *useCases
-                )
-            return@trace camera
-        }
+    ): Camera {
+        return lifecycleCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, *useCases)
+    }
 
-    /**
-     * Binds a [UseCaseGroup] to a [LifecycleOwner].
-     *
-     * Similar to [bindToLifecycle], with the addition that the bound collection of [UseCase] share
-     * parameters defined by [UseCaseGroup] such as consistent camera sensor rect across all
-     * [UseCase]s.
-     *
-     * If one [UseCase] is in multiple [UseCaseGroup]s, it will be linked to the [UseCaseGroup] in
-     * the latest [bindToLifecycle] call.
-     *
-     * @throws UnsupportedOperationException If the camera is configured in concurrent mode.
-     */
     @MainThread
-    public override fun bindToLifecycle(
+    override fun bindToLifecycle(
         lifecycleOwner: LifecycleOwner,
         cameraSelector: CameraSelector,
         useCaseGroup: UseCaseGroup
-    ): Camera =
-        trace("CX:bindToLifecycle-UseCaseGroup") {
-            if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) {
-                throw UnsupportedOperationException(
-                    "bindToLifecycle for single camera is not supported in concurrent camera mode, " +
-                        "call unbindAll() first."
-                )
-            }
-            cameraOperatingMode = CAMERA_OPERATING_MODE_SINGLE
-            val camera =
-                bindToLifecycle(
-                    lifecycleOwner,
-                    cameraSelector,
-                    null,
-                    CompositionSettings.DEFAULT,
-                    CompositionSettings.DEFAULT,
-                    useCaseGroup.viewPort,
-                    useCaseGroup.effects,
-                    *useCaseGroup.useCases.toTypedArray<UseCase>()
-                )
-            return@trace camera
-        }
-
-    /**
-     * Binds list of [SingleCameraConfig]s to [LifecycleOwner].
-     *
-     * The concurrent camera is only supporting two cameras currently. If the input list of
-     * [SingleCameraConfig]s have less or more than two [SingleCameraConfig]s,
-     * [IllegalArgumentException] will be thrown. If cameras are already used by other [UseCase]s,
-     * [UnsupportedOperationException] will be thrown.
-     *
-     * A logical camera is a grouping of two or more of those physical cameras. See
-     * [Multi-camera API](https://developer.android.com/media/camera/camera2/multi-camera)
-     *
-     * If we want to open concurrent logical cameras, which are one front camera and one back
-     * camera, the device needs to support [PackageManager.FEATURE_CAMERA_CONCURRENT]. To set up
-     * concurrent logical camera, call [availableConcurrentCameraInfos] to get the list of available
-     * combinations of concurrent cameras. Each sub-list contains the [CameraInfo]s for a
-     * combination of cameras that can be operated concurrently. Each logical camera can have its
-     * own [UseCase]s and [LifecycleOwner]. See
-     * [CameraX lifecycles]({@docRoot}training/camerax/architecture#lifecycles)
-     *
-     * If the concurrent logical cameras are binding the same preview and video capture use cases,
-     * the concurrent cameras video recording will be supported. The concurrent camera preview
-     * stream will be shared with video capture and record the concurrent cameras streams as a
-     * composited stream. The [CompositionSettings] can be used to configure the position of each
-     * camera stream and different layouts can be built. See [CompositionSettings] for more details.
-     *
-     * If we want to open concurrent physical cameras, which are two front cameras or two back
-     * cameras, the device needs to support physical cameras and the capability could be checked via
-     * [CameraInfo.isLogicalMultiCameraSupported]. Each physical cameras can have its own [UseCase]s
-     * but needs to have the same [LifecycleOwner], otherwise [IllegalArgumentException] will be
-     * thrown.
-     *
-     * If we want to open one physical camera, for example ultra wide, we just need to set physical
-     * camera id in [CameraSelector] and bind to lifecycle. All CameraX features will work normally
-     * when only a single physical camera is used.
-     *
-     * If we want to open multiple physical cameras, we need to have multiple [CameraSelector]s,
-     * each in one [SingleCameraConfig] and set physical camera id, then bind to lifecycle with the
-     * [SingleCameraConfig]s. Internally each physical camera id will be set on [UseCase], for
-     * example, [Preview] and call
-     * [android.hardware.camera2.params.OutputConfiguration.setPhysicalCameraId].
-     *
-     * Currently only two physical cameras for the same logical camera id are allowed and the device
-     * needs to support physical cameras by checking [CameraInfo.isLogicalMultiCameraSupported]. In
-     * addition, there is no guarantee or API to query whether the device supports multiple physical
-     * camera opening or not. Internally the library checks
-     * [android.hardware.camera2.CameraDevice.isSessionConfigurationSupported], if the device does
-     * not support the multiple physical camera configuration, [IllegalArgumentException] will be
-     * thrown.
-     *
-     * @param singleCameraConfigs Input list of [SingleCameraConfig]s.
-     * @return Output [ConcurrentCamera] instance.
-     * @throws IllegalArgumentException If less or more than two camera configs are provided.
-     * @throws UnsupportedOperationException If device is not supporting concurrent camera or
-     *   cameras are already used by other [UseCase]s.
-     * @see ConcurrentCamera
-     * @see availableConcurrentCameraInfos
-     * @see CameraInfo.isLogicalMultiCameraSupported
-     * @see CameraInfo.getPhysicalCameraInfos
-     */
-    @MainThread
-    public override fun bindToLifecycle(
-        singleCameraConfigs: List<SingleCameraConfig?>
-    ): ConcurrentCamera =
-        trace("CX:bindToLifecycle-Concurrent") {
-            if (singleCameraConfigs.size < 2) {
-                throw IllegalArgumentException("Concurrent camera needs two camera configs.")
-            }
-
-            if (singleCameraConfigs.size > 2) {
-                throw IllegalArgumentException(
-                    "Concurrent camera is only supporting two cameras at maximum."
-                )
-            }
-
-            val firstCameraConfig = singleCameraConfigs[0]!!
-            val secondCameraConfig = singleCameraConfigs[1]!!
-
-            val cameras: MutableList<Camera> = ArrayList()
-            if (
-                firstCameraConfig.cameraSelector.lensFacing ==
-                    secondCameraConfig.cameraSelector.lensFacing
-            ) {
-                if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) {
-                    throw UnsupportedOperationException(
-                        "Camera is already running, call unbindAll() before binding more cameras."
-                    )
-                }
-                if (
-                    firstCameraConfig.lifecycleOwner != secondCameraConfig.lifecycleOwner ||
-                        firstCameraConfig.useCaseGroup.viewPort !=
-                            secondCameraConfig.useCaseGroup.viewPort ||
-                        firstCameraConfig.useCaseGroup.effects !=
-                            secondCameraConfig.useCaseGroup.effects
-                ) {
-                    throw IllegalArgumentException(
-                        "Two camera configs need to have the same lifecycle owner, view port and " +
-                            "effects."
-                    )
-                }
-                val lifecycleOwner = firstCameraConfig.lifecycleOwner
-                val cameraSelector = firstCameraConfig.cameraSelector
-                val viewPort = firstCameraConfig.useCaseGroup.viewPort
-                val effects = firstCameraConfig.useCaseGroup.effects
-                val useCases: MutableList<UseCase> = ArrayList()
-                for (config: SingleCameraConfig? in singleCameraConfigs) {
-                    // Connect physical camera id with use case.
-                    for (useCase: UseCase in config!!.useCaseGroup.useCases) {
-                        config.cameraSelector.physicalCameraId?.let {
-                            useCase.setPhysicalCameraId(it)
-                        }
-                    }
-                    useCases.addAll(config.useCaseGroup.useCases)
-                }
-
-                cameraOperatingMode = CAMERA_OPERATING_MODE_SINGLE
-                val camera =
-                    bindToLifecycle(
-                        lifecycleOwner,
-                        cameraSelector,
-                        null,
-                        CompositionSettings.DEFAULT,
-                        CompositionSettings.DEFAULT,
-                        viewPort,
-                        effects,
-                        *useCases.toTypedArray<UseCase>()
-                    )
-                cameras.add(camera)
-            } else {
-                if (!mContext!!.packageManager.hasSystemFeature(FEATURE_CAMERA_CONCURRENT)) {
-                    throw UnsupportedOperationException(
-                        "Concurrent camera is not supported on the device."
-                    )
-                }
-
-                if (cameraOperatingMode == CAMERA_OPERATING_MODE_SINGLE) {
-                    throw UnsupportedOperationException(
-                        "Camera is already running, call unbindAll() before binding more cameras."
-                    )
-                }
-
-                val cameraInfosToBind: MutableList<CameraInfo> = ArrayList()
-                val firstCameraInfo: CameraInfo
-                val secondCameraInfo: CameraInfo
-                try {
-                    firstCameraInfo = getCameraInfo(firstCameraConfig.cameraSelector)
-                    secondCameraInfo = getCameraInfo(secondCameraConfig.cameraSelector)
-                } catch (e: IllegalArgumentException) {
-                    throw IllegalArgumentException("Invalid camera selectors in camera configs.")
-                }
-                cameraInfosToBind.add(firstCameraInfo)
-                cameraInfosToBind.add(secondCameraInfo)
-                if (
-                    activeConcurrentCameraInfos.isNotEmpty() &&
-                        cameraInfosToBind != activeConcurrentCameraInfos
-                ) {
-                    throw UnsupportedOperationException(
-                        "Cameras are already running, call unbindAll() before binding more cameras."
-                    )
-                }
-
-                cameraOperatingMode = CAMERA_OPERATING_MODE_CONCURRENT
-
-                // For dual camera video capture, we are only supporting two use cases:
-                // Preview + VideoCapture. If ImageCapture support is added, the validation logic
-                // will be updated accordingly.
-                var isDualCameraVideoCapture = false
-                if (
-                    Objects.equals(
-                        firstCameraConfig.useCaseGroup.useCases,
-                        secondCameraConfig.useCaseGroup.useCases
-                    ) && firstCameraConfig.useCaseGroup.useCases.size == 2
-                ) {
-                    val useCase0 = firstCameraConfig.useCaseGroup.useCases[0]
-                    val useCase1 = firstCameraConfig.useCaseGroup.useCases[1]
-                    isDualCameraVideoCapture =
-                        (isVideoCapture(useCase0) && isPreview(useCase1)) ||
-                            (isPreview(useCase0) && isVideoCapture(useCase1))
-                }
-
-                if (isDualCameraVideoCapture) {
-                    cameras.add(
-                        bindToLifecycle(
-                            firstCameraConfig.lifecycleOwner,
-                            firstCameraConfig.cameraSelector,
-                            secondCameraConfig.cameraSelector,
-                            firstCameraConfig.compositionSettings,
-                            secondCameraConfig.compositionSettings,
-                            firstCameraConfig.useCaseGroup.viewPort,
-                            firstCameraConfig.useCaseGroup.effects,
-                            *firstCameraConfig.useCaseGroup.useCases.toTypedArray<UseCase>(),
-                        )
-                    )
-                } else {
-                    for (config: SingleCameraConfig? in singleCameraConfigs) {
-                        val camera =
-                            bindToLifecycle(
-                                config!!.lifecycleOwner,
-                                config.cameraSelector,
-                                null,
-                                CompositionSettings.DEFAULT,
-                                CompositionSettings.DEFAULT,
-                                config.useCaseGroup.viewPort,
-                                config.useCaseGroup.effects,
-                                *config.useCaseGroup.useCases.toTypedArray<UseCase>()
-                            )
-                        cameras.add(camera)
-                    }
-                }
-                activeConcurrentCameraInfos = cameraInfosToBind
-            }
-            return@trace ConcurrentCamera(cameras)
-        }
-
-    private fun isVideoCapture(useCase: UseCase): Boolean {
-        return useCase.currentConfig.containsOption(UseCaseConfig.OPTION_CAPTURE_TYPE) &&
-            useCase.currentConfig.captureType == CaptureType.VIDEO_CAPTURE
+    ): Camera {
+        return lifecycleCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)
     }
 
-    private fun isPreview(useCase: UseCase): Boolean {
-        return useCase is Preview
-    }
-
-    /**
-     * Binds [ViewPort] and a collection of [UseCase] to a [LifecycleOwner].
-     *
-     * The state of the lifecycle will determine when the cameras are open, started, stopped and
-     * closed. When started, the use cases receive camera data.
-     *
-     * Binding to a [LifecycleOwner] in state currently in [Lifecycle.State.STARTED] or greater will
-     * also initialize and start data capture. If the camera was already running this may cause a
-     * new initialization to occur temporarily stopping data from the camera before restarting it.
-     *
-     * Multiple use cases can be bound via adding them all to a single [bindToLifecycle] call, or by
-     * using multiple [bindToLifecycle] calls. Using a single call that includes all the use cases
-     * helps to set up a camera session correctly for all uses cases, such as by allowing
-     * determination of resolutions depending on all the use cases bound being bound. If the use
-     * cases are bound separately, it will find the supported resolution with the priority depending
-     * on the binding sequence. If the use cases are bound with a single call, it will find the
-     * supported resolution with the priority in sequence of [ImageCapture], [Preview] and then
-     * [ImageAnalysis]. The resolutions that can be supported depends on the camera device hardware
-     * level that there are some default guaranteed resolutions listed in
-     * [android.hardware.camera2.CameraDevice.createCaptureSession].
-     *
-     * Currently up to 3 use cases may be bound to a [Lifecycle] at any time. Exceeding capability
-     * of target camera device will throw an IllegalArgumentException.
-     *
-     * A [UseCase] should only be bound to a single lifecycle and camera selector a time. Attempting
-     * to bind a use case to a lifecycle when it is already bound to another lifecycle is an error,
-     * and the use case binding will not change. Attempting to bind the same use case to multiple
-     * camera selectors is also an error and will not change the binding.
-     *
-     * If different use cases are bound to different camera selectors that resolve to distinct
-     * cameras, but the same lifecycle, only one of the cameras will operate at a time. The
-     * non-operating camera will not become active until it is the only camera with use cases bound.
-     *
-     * The [Camera] returned is determined by the given camera selector, plus other internal
-     * requirements, possibly from use case configurations. The camera returned from
-     * [bindToLifecycle] may differ from the camera determined solely by a camera selector. If the
-     * camera selector can't resolve a camera under the requirements, an [IllegalArgumentException]
-     * will be thrown.
-     *
-     * Only [UseCase] bound to latest active [Lifecycle] can keep alive. [UseCase] bound to other
-     * [Lifecycle] will be stopped.
-     *
-     * @param lifecycleOwner The [LifecycleOwner] which controls the lifecycle transitions of the
-     *   use cases.
-     * @param primaryCameraSelector The primary camera selector which determines the camera to use
-     *   for set of use cases.
-     * @param secondaryCameraSelector The secondary camera selector in dual camera case.
-     * @param primaryCompositionSettings The composition settings for the primary camera.
-     * @param secondaryCompositionSettings The composition settings for the secondary camera.
-     * @param viewPort The viewPort which represents the visible camera sensor rect.
-     * @param effects The effects applied to the camera outputs.
-     * @param useCases The use cases to bind to a lifecycle.
-     * @return The [Camera] instance which is determined by the camera selector and internal
-     *   requirements.
-     * @throws IllegalStateException If the use case has already been bound to another lifecycle or
-     *   method is not called on main thread.
-     * @throws IllegalArgumentException If the provided camera selector is unable to resolve a
-     *   camera to be used for the given use cases.
-     */
-    @Suppress("unused")
-    internal fun bindToLifecycle(
-        lifecycleOwner: LifecycleOwner,
-        primaryCameraSelector: CameraSelector,
-        secondaryCameraSelector: CameraSelector?,
-        primaryCompositionSettings: CompositionSettings,
-        secondaryCompositionSettings: CompositionSettings,
-        viewPort: ViewPort?,
-        effects: List<CameraEffect?>,
-        vararg useCases: UseCase?
-    ): Camera =
-        trace("CX:bindToLifecycle-internal") {
-            Threads.checkMainThread()
-            // TODO(b/153096869): override UseCase's target rotation.
-
-            // Get the LifecycleCamera if existed.
-            val primaryCameraInternal =
-                primaryCameraSelector.select(mCameraX!!.cameraRepository.cameras)
-            primaryCameraInternal.setPrimary(true)
-            val primaryRestrictedCameraInfo =
-                getCameraInfo(primaryCameraSelector) as RestrictedCameraInfo
-
-            var secondaryCameraInternal: CameraInternal? = null
-            var secondaryRestrictedCameraInfo: RestrictedCameraInfo? = null
-            if (secondaryCameraSelector != null) {
-                secondaryCameraInternal =
-                    secondaryCameraSelector.select(mCameraX!!.cameraRepository.cameras)
-                secondaryCameraInternal.setPrimary(false)
-                secondaryRestrictedCameraInfo =
-                    getCameraInfo(secondaryCameraSelector) as RestrictedCameraInfo
-            }
-
-            var lifecycleCameraToBind =
-                mLifecycleCameraRepository.getLifecycleCamera(
-                    lifecycleOwner,
-                    CameraUseCaseAdapter.generateCameraId(
-                        primaryRestrictedCameraInfo,
-                        secondaryRestrictedCameraInfo
-                    )
-                )
-
-            // Check if there's another camera that has already been bound.
-            val lifecycleCameras = mLifecycleCameraRepository.lifecycleCameras
-            useCases.filterNotNull().forEach { useCase ->
-                for (lifecycleCamera: LifecycleCamera in lifecycleCameras) {
-                    if (
-                        lifecycleCamera.isBound(useCase) && lifecycleCamera != lifecycleCameraToBind
-                    ) {
-                        throw IllegalStateException(
-                            String.format(
-                                "Use case %s already bound to a different lifecycle.",
-                                useCase
-                            )
-                        )
-                    }
-                }
-            }
-
-            // Create the LifecycleCamera if there's no existing one that can be used.
-            if (lifecycleCameraToBind == null) {
-                lifecycleCameraToBind =
-                    mLifecycleCameraRepository.createLifecycleCamera(
-                        lifecycleOwner,
-                        CameraUseCaseAdapter(
-                            primaryCameraInternal,
-                            secondaryCameraInternal,
-                            primaryRestrictedCameraInfo,
-                            secondaryRestrictedCameraInfo,
-                            primaryCompositionSettings,
-                            secondaryCompositionSettings,
-                            mCameraX!!.cameraFactory.cameraCoordinator,
-                            mCameraX!!.cameraDeviceSurfaceManager,
-                            mCameraX!!.defaultConfigFactory
-                        )
-                    )
-            }
-
-            if (useCases.isEmpty()) {
-                return@trace lifecycleCameraToBind!!
-            }
-
-            mLifecycleCameraRepository.bindToLifecycleCamera(
-                lifecycleCameraToBind!!,
-                viewPort,
-                effects,
-                listOf(*useCases),
-                mCameraX!!.cameraFactory.cameraCoordinator
-            )
-
-            return@trace lifecycleCameraToBind
-        }
-
-    public override fun isBound(useCase: UseCase): Boolean {
-        for (lifecycleCamera: LifecycleCamera in mLifecycleCameraRepository.lifecycleCameras) {
-            if (lifecycleCamera.isBound(useCase)) {
-                return true
-            }
-        }
-
-        return false
-    }
-
-    /**
-     * Unbinds all specified use cases from the lifecycle.
-     *
-     * This will initiate a close of every open camera which has zero [UseCase] associated with it
-     * at the end of this call.
-     *
-     * If a use case in the argument list is not bound, then it is simply ignored.
-     *
-     * After unbinding a UseCase, the UseCase can be and bound to another [Lifecycle] however
-     * listeners and settings should be reset by the application.
-     *
-     * @param useCases The collection of use cases to remove.
-     * @throws IllegalStateException If not called on main thread.
-     * @throws UnsupportedOperationException If called in concurrent mode.
-     */
     @MainThread
-    public override fun unbind(vararg useCases: UseCase?): Unit =
-        trace("CX:unbind") {
-            Threads.checkMainThread()
-
-            if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) {
-                throw UnsupportedOperationException(
-                    "Unbind usecase is not supported in concurrent camera mode, call unbindAll() first."
-                )
-            }
-
-            mLifecycleCameraRepository.unbind(listOf(*useCases))
-        }
-
-    @MainThread
-    public override fun unbindAll(): Unit =
-        trace("CX:unbindAll") {
-            Threads.checkMainThread()
-            cameraOperatingMode = CAMERA_OPERATING_MODE_UNSPECIFIED
-            mLifecycleCameraRepository.unbindAll()
-        }
-
-    @Throws(CameraInfoUnavailableException::class)
-    override fun hasCamera(cameraSelector: CameraSelector): Boolean =
-        trace("CX:hasCamera") {
-            try {
-                cameraSelector.select(mCameraX!!.cameraRepository.cameras)
-            } catch (e: IllegalArgumentException) {
-                return@trace false
-            }
-
-            return@trace true
-        }
+    override fun bindToLifecycle(
+        singleCameraConfigs: List<ConcurrentCamera.SingleCameraConfig?>
+    ): ConcurrentCamera {
+        return lifecycleCameraProvider.bindToLifecycle(singleCameraConfigs)
+    }
 
     override val availableCameraInfos: List<CameraInfo>
-        get() =
-            trace("CX:getAvailableCameraInfos") {
-                val availableCameraInfos: MutableList<CameraInfo> = ArrayList()
-                val cameras: Set<CameraInternal> = mCameraX!!.cameraRepository.cameras
-                for (camera: CameraInternal in cameras) {
-                    availableCameraInfos.add(camera.cameraInfo)
-                }
-                return@trace availableCameraInfos
-            }
+        get() = lifecycleCameraProvider.availableCameraInfos
 
     final override val availableConcurrentCameraInfos: List<List<CameraInfo>>
-        get() =
-            trace("CX:getAvailableConcurrentCameraInfos") {
-                requireNonNull(mCameraX)
-                requireNonNull(mCameraX!!.cameraFactory.cameraCoordinator)
-                val concurrentCameraSelectorLists =
-                    mCameraX!!.cameraFactory.cameraCoordinator.concurrentCameraSelectors
-
-                val availableConcurrentCameraInfos: MutableList<List<CameraInfo>> = ArrayList()
-                for (cameraSelectors in concurrentCameraSelectorLists) {
-                    val cameraInfos: MutableList<CameraInfo> = ArrayList()
-                    for (cameraSelector in cameraSelectors) {
-                        var cameraInfo: CameraInfo
-                        try {
-                            cameraInfo = getCameraInfo(cameraSelector)
-                        } catch (e: IllegalArgumentException) {
-                            continue
-                        }
-                        cameraInfos.add(cameraInfo)
-                    }
-                    availableConcurrentCameraInfos.add(cameraInfos)
-                }
-                return@trace availableConcurrentCameraInfos
-            }
-
-    override fun getCameraInfo(cameraSelector: CameraSelector): CameraInfo =
-        trace("CX:getCameraInfo") {
-            val cameraInfoInternal =
-                cameraSelector.select(mCameraX!!.cameraRepository.cameras).cameraInfoInternal
-            val cameraConfig = getCameraConfig(cameraSelector, cameraInfoInternal)
-
-            val key =
-                CameraUseCaseAdapter.CameraId.create(
-                    cameraInfoInternal.cameraId,
-                    cameraConfig.compatibilityId
-                )
-            var restrictedCameraInfo: RestrictedCameraInfo?
-            synchronized(mLock) {
-                restrictedCameraInfo = mCameraInfoMap[key]
-                if (restrictedCameraInfo == null) {
-                    restrictedCameraInfo = RestrictedCameraInfo(cameraInfoInternal, cameraConfig)
-                    mCameraInfoMap[key] = restrictedCameraInfo!!
-                }
-            }
-
-            return@trace restrictedCameraInfo!!
-        }
+        get() = lifecycleCameraProvider.availableConcurrentCameraInfos
 
     final override val isConcurrentCameraModeOn: Boolean
-        @MainThread get() = cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT
+        @MainThread get() = lifecycleCameraProvider.isConcurrentCameraModeOn
 
-    private fun getOrCreateCameraXInstance(context: Context): ListenableFuture<CameraX> {
-        synchronized(mLock) {
-            if (mCameraXInitializeFuture != null) {
-                return mCameraXInitializeFuture as ListenableFuture<CameraX>
-            }
-            val cameraX = CameraX(context, mCameraXConfigProvider)
-
-            mCameraXInitializeFuture =
-                CallbackToFutureAdapter.getFuture { completer ->
-                    synchronized(mLock) {
-                        val future: ListenableFuture<Void> =
-                            FutureChain.from(mCameraXShutdownFuture)
-                                .transformAsync(
-                                    { cameraX.initializeFuture },
-                                    CameraXExecutors.directExecutor()
-                                )
-                        Futures.addCallback(
-                            future,
-                            object : FutureCallback<Void?> {
-                                override fun onSuccess(result: Void?) {
-                                    completer.set(cameraX)
-                                }
-
-                                override fun onFailure(t: Throwable) {
-                                    completer.setException(t)
-                                }
-                            },
-                            CameraXExecutors.directExecutor()
-                        )
-                    }
-
-                    "ProcessCameraProvider-initializeCameraX"
-                }
-
-            return mCameraXInitializeFuture as ListenableFuture<CameraX>
-        }
+    @Throws(CameraInfoUnavailableException::class)
+    override fun hasCamera(cameraSelector: CameraSelector): Boolean {
+        return lifecycleCameraProvider.hasCamera(cameraSelector)
     }
 
-    private fun configureInstanceInternal(cameraXConfig: CameraXConfig) =
-        trace("CX:configureInstanceInternal") {
-            synchronized(mLock) {
-                Preconditions.checkNotNull(cameraXConfig)
-                Preconditions.checkState(
-                    mCameraXConfigProvider == null,
-                    "CameraX has already been configured. To use a different configuration, " +
-                        "shutdown() must be called."
-                )
-                mCameraXConfigProvider = CameraXConfig.Provider { cameraXConfig }
-            }
-        }
-
-    private fun getCameraConfig(
-        cameraSelector: CameraSelector,
-        cameraInfo: CameraInfo
-    ): CameraConfig {
-        var cameraConfig: CameraConfig? = null
-        for (cameraFilter: CameraFilter in cameraSelector.cameraFilterSet) {
-            if (cameraFilter.identifier != CameraFilter.DEFAULT_ID) {
-                val extendedCameraConfig =
-                    ExtendedCameraConfigProviderStore.getConfigProvider(cameraFilter.identifier)
-                        .getConfig(cameraInfo, (mContext)!!)
-                if (extendedCameraConfig == null) { // ignore IDs unrelated to camera configs.
-                    continue
-                }
-
-                // Only allows one camera config now.
-                if (cameraConfig != null) {
-                    throw IllegalArgumentException(
-                        "Cannot apply multiple extended camera configs at the same time."
-                    )
-                }
-                cameraConfig = extendedCameraConfig
-            }
-        }
-
-        if (cameraConfig == null) {
-            cameraConfig = CameraConfigs.defaultConfig()
-        }
-        return cameraConfig
+    override fun getCameraInfo(cameraSelector: CameraSelector): CameraInfo {
+        return lifecycleCameraProvider.getCameraInfo(cameraSelector)
     }
 
-    private fun setCameraX(cameraX: CameraX) {
-        mCameraX = cameraX
+    // TODO: Remove the annotation when LifecycleCameraProvider is ready to be public.
+    @VisibleForTesting
+    override fun shutdownAsync(): ListenableFuture<Void> {
+        return lifecycleCameraProvider.shutdownAsync()
     }
 
-    private fun setContext(context: Context) {
-        mContext = context
+    private fun initAsync(context: Context): ListenableFuture<Void> {
+        return lifecycleCameraProvider.initAsync(context, null)
     }
 
-    @get:CameraOperatingMode
-    private var cameraOperatingMode: Int
-        get() {
-            if (mCameraX == null) {
-                return CAMERA_OPERATING_MODE_UNSPECIFIED
-            }
-            return mCameraX!!.cameraFactory.cameraCoordinator.cameraOperatingMode
-        }
-        private set(cameraOperatingMode) {
-            if (mCameraX == null) {
-                return
-            }
-            mCameraX!!.cameraFactory.cameraCoordinator.cameraOperatingMode = cameraOperatingMode
-        }
-
-    private var activeConcurrentCameraInfos: List<CameraInfo>
-        get() {
-            if (mCameraX == null) {
-                return java.util.ArrayList()
-            }
-            return mCameraX!!.cameraFactory.cameraCoordinator.activeConcurrentCameraInfos
-        }
-        private set(cameraInfos) {
-            if (mCameraX == null) {
-                return
-            }
-            mCameraX!!.cameraFactory.cameraCoordinator.activeConcurrentCameraInfos = cameraInfos
-        }
+    private fun configure(cameraXConfig: CameraXConfig) {
+        return lifecycleCameraProvider.configure(cameraXConfig)
+    }
 
     public companion object {
-        private val sAppInstance = ProcessCameraProvider()
+        private val sAppInstance = ProcessCameraProvider(LifecycleCameraProviderImpl())
 
         /**
          * Retrieves the ProcessCameraProvider associated with the current process.
@@ -859,12 +170,8 @@
         public fun getInstance(context: Context): ListenableFuture<ProcessCameraProvider> {
             Preconditions.checkNotNull(context)
             return Futures.transform(
-                sAppInstance.getOrCreateCameraXInstance(context),
-                { cameraX ->
-                    sAppInstance.setCameraX(cameraX)
-                    sAppInstance.setContext(ContextUtil.getApplicationContext(context))
-                    sAppInstance
-                },
+                sAppInstance.initAsync(context),
+                { sAppInstance },
                 CameraXExecutors.directExecutor()
             )
         }
@@ -892,7 +199,7 @@
          * method from library code is not recommended** as the application owner should ultimately
          * be in control of singleton configuration.
          *
-         * @param cameraXConfig configuration options for the singleton process camera provider
+         * @param cameraXConfig The configuration options for the singleton process camera provider
          *   instance.
          * @throws IllegalStateException If the camera provider has already been configured by a
          *   previous call to `configureInstance()` or [getInstance].
@@ -900,6 +207,6 @@
         @JvmStatic
         @ExperimentalCameraProviderConfiguration
         public fun configureInstance(cameraXConfig: CameraXConfig): Unit =
-            trace("CX:configureInstance") { sAppInstance.configureInstanceInternal(cameraXConfig) }
+            trace("CX:configureInstance") { sAppInstance.configure(cameraXConfig) }
     }
 }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeImageCaptureCallback.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageCapturedCallback.kt
similarity index 64%
rename from camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeImageCaptureCallback.kt
rename to camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageCapturedCallback.kt
index b600a40..135f64c 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeImageCaptureCallback.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageCapturedCallback.kt
@@ -25,14 +25,25 @@
 import androidx.camera.core.impl.utils.Exif
 import com.google.common.truth.Truth
 import java.io.ByteArrayInputStream
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.withTimeoutOrNull
 
-private const val CAPTURE_TIMEOUT = 15_000.toLong() //  15 seconds
+/**
+ * A fake implementation of the [ImageCapture.OnImageCapturedCallback] that is used for test.
+ *
+ * @param captureCount Number of captures to wait for.
+ * @property closeImageOnSuccess Whether to close images immediately on [onCaptureSuccess]
+ *   callbacks. This is true by default. If set to false, it is the user's responsibility to close
+ *   the images.
+ */
+public class FakeOnImageCapturedCallback(
+    captureCount: Int = 1,
+    private val closeImageOnSuccess: Boolean = true
+) : ImageCapture.OnImageCapturedCallback() {
+    public data class CapturedImage(val image: ImageProxy, val properties: ImageProperties)
 
-/** A fake implementation of the [ImageCapture.OnImageCapturedCallback] and used for test. */
-public class FakeImageCaptureCallback(captureCount: Int = 1) :
-    ImageCapture.OnImageCapturedCallback() {
     /** Data class of various image properties which are tested. */
     public data class ImageProperties(
         val size: Size? = null,
@@ -43,20 +54,34 @@
     )
 
     private val latch = CountdownDeferred(captureCount)
-    public val results: MutableList<ImageProperties> = mutableListOf()
+
+    /**
+     * List of [CapturedImage] obtained in [onCaptureSuccess] callback.
+     *
+     * If [closeImageOnSuccess] is true, the [CapturedImage.image] will be closed as soon as
+     * `onCaptureSuccess` is invoked. Otherwise, it will be the user's responsibility to close the
+     * images.
+     */
+    public val results: MutableList<CapturedImage> = mutableListOf()
     public val errors: MutableList<ImageCaptureException> = mutableListOf()
 
     override fun onCaptureSuccess(image: ImageProxy) {
         results.add(
-            ImageProperties(
-                size = Size(image.width, image.height),
-                format = image.format,
-                rotationDegrees = image.imageInfo.rotationDegrees,
-                cropRect = image.cropRect,
-                exif = getExif(image),
+            CapturedImage(
+                image = image,
+                properties =
+                    ImageProperties(
+                        size = Size(image.width, image.height),
+                        format = image.format,
+                        rotationDegrees = image.imageInfo.rotationDegrees,
+                        cropRect = image.cropRect,
+                        exif = getExif(image),
+                    )
             )
         )
-        image.close()
+        if (closeImageOnSuccess) {
+            image.close()
+        }
         latch.countDown()
     }
 
@@ -76,12 +101,12 @@
         return null
     }
 
-    public suspend fun awaitCaptures(timeout: Long = CAPTURE_TIMEOUT) {
+    public suspend fun awaitCaptures(timeout: Duration = CAPTURE_TIMEOUT) {
         Truth.assertThat(withTimeoutOrNull(timeout) { latch.await() }).isNotNull()
     }
 
     public suspend fun awaitCapturesAndAssert(
-        timeout: Long = CAPTURE_TIMEOUT,
+        timeout: Duration = CAPTURE_TIMEOUT,
         capturedImagesCount: Int = 0,
         errorsCount: Int = 0
     ) {
@@ -110,4 +135,8 @@
             deferredItems.forEach { it.await() }
         }
     }
+
+    public companion object {
+        private val CAPTURE_TIMEOUT = 15.seconds
+    }
 }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageSavedCallback.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageSavedCallback.kt
new file mode 100644
index 0000000..4ddec70
--- /dev/null
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageSavedCallback.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2024 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.testing.impl.fakes
+
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.ImageCaptureException
+import com.google.common.truth.Truth
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.withTimeoutOrNull
+
+/**
+ * A fake implementation of the [ImageCapture.OnImageCapturedCallback] that is used for test.
+ *
+ * @param captureCount Number of captures to wait for.
+ */
+public class FakeOnImageSavedCallback(captureCount: Int = 1) : ImageCapture.OnImageSavedCallback {
+    private val latch = CountdownDeferred(captureCount)
+    public val results: MutableList<ImageCapture.OutputFileResults> = mutableListOf()
+    public val errors: MutableList<ImageCaptureException> = mutableListOf()
+
+    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
+        results.add(outputFileResults)
+        latch.countDown()
+    }
+
+    override fun onError(exception: ImageCaptureException) {
+        errors.add(exception)
+        latch.countDown()
+    }
+
+    public suspend fun awaitCaptures(timeout: Duration = CAPTURE_TIMEOUT) {
+        Truth.assertThat(withTimeoutOrNull(timeout) { latch.await() }).isNotNull()
+    }
+
+    public suspend fun awaitCapturesAndAssert(
+        timeout: Duration = CAPTURE_TIMEOUT,
+        capturedImagesCount: Int = 0,
+        errorsCount: Int = 0
+    ) {
+        Truth.assertThat(withTimeoutOrNull(timeout) { latch.await() }).isNotNull()
+        Truth.assertThat(results.size).isEqualTo(capturedImagesCount)
+        Truth.assertThat(errors.size).isEqualTo(errorsCount)
+    }
+
+    private class CountdownDeferred(val count: Int) {
+
+        private val deferredItems =
+            mutableListOf<CompletableDeferred<Unit>>().apply {
+                repeat(count) { add(CompletableDeferred()) }
+            }
+        private var index = 0
+
+        fun countDown() {
+            if (index < count) {
+                deferredItems[index++].complete(Unit)
+            } else {
+                throw IllegalStateException("Countdown already finished")
+            }
+        }
+
+        suspend fun await() {
+            deferredItems.forEach { it.await() }
+        }
+    }
+
+    public companion object {
+        private val CAPTURE_TIMEOUT = 15.seconds
+    }
+}
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 0775266..8e4f80d 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
@@ -140,6 +140,11 @@
                 SizeCannotEncodeVideoQuirk.load())) {
             quirks.add(new SizeCannotEncodeVideoQuirk());
         }
+        if (quirkSettings.shouldEnableQuirk(
+                PreviewBlackScreenQuirk.class,
+                PreviewBlackScreenQuirk.load())) {
+            quirks.add(new PreviewBlackScreenQuirk());
+        }
         return quirks;
     }
 }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/PreviewBlackScreenQuirk.kt b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/PreviewBlackScreenQuirk.kt
new file mode 100644
index 0000000..67ed713
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/PreviewBlackScreenQuirk.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024 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.annotation.SuppressLint
+import android.os.Build
+import androidx.camera.core.internal.compat.quirk.SurfaceProcessingQuirk
+
+/**
+ * QuirkSummary
+ * - Bug Id: b/361477717
+ * - Description: Quirk indicates Preview is black screen when binding with VideoCapture.
+ * - Device(s): Motorola Edge 20 Fusion.
+ */
+@SuppressLint("CameraXQuirksClassDetector")
+public class PreviewBlackScreenQuirk : SurfaceProcessingQuirk {
+
+    public companion object {
+
+        @JvmStatic
+        public fun load(): Boolean {
+            return isMotorolaEdge20Fusion
+        }
+
+        private val isMotorolaEdge20Fusion: Boolean =
+            Build.BRAND.equals("motorola", ignoreCase = true) &&
+                Build.MODEL.equals("motorola edge 20 fusion", ignoreCase = true)
+    }
+}
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
index 346e8e9..4c1e375 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
@@ -84,8 +84,8 @@
 import androidx.camera.testing.impl.CoreAppTestUtil
 import androidx.camera.testing.impl.SurfaceTextureProvider
 import androidx.camera.testing.impl.WakelockEmptyActivityRule
-import androidx.camera.testing.impl.fakes.FakeImageCaptureCallback
 import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
+import androidx.camera.testing.impl.fakes.FakeOnImageCapturedCallback
 import androidx.camera.testing.impl.fakes.FakeSessionProcessor
 import androidx.camera.testing.impl.mocks.MockScreenFlash
 import androidx.camera.video.Recorder
@@ -103,6 +103,8 @@
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.atomic.AtomicInteger
 import kotlin.math.abs
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.Dispatchers
@@ -126,7 +128,7 @@
 private val BACK_SELECTOR = CameraSelector.DEFAULT_BACK_CAMERA
 private val FRONT_SELECTOR = CameraSelector.DEFAULT_FRONT_CAMERA
 private const val BACK_LENS_FACING = CameraSelector.LENS_FACING_BACK
-private const val CAPTURE_TIMEOUT = 15_000.toLong() //  15 seconds
+private val CAPTURE_TIMEOUT = 15.seconds
 private const val TOLERANCE = 1e-3f
 private val EXIF_GAINMAP_PATTERNS =
     listOf(
@@ -231,14 +233,14 @@
         }
 
         // Act.
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
         useCase.takePicture(mainExecutor, callback)
 
         // Assert.
         // Wait for the signal that the image has been captured.
         callback.awaitCapturesAndAssert(capturedImagesCount = 1)
 
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
         var sizeEnvelope = imageProperties.size
 
         // Some devices may not be able to fit the requested resolution. In this case, the returned
@@ -311,13 +313,13 @@
 
         if (camera.cameraInfo.isZslSupported) {
             val numImages = 5
-            val callback = FakeImageCaptureCallback(captureCount = numImages)
+            val callback = FakeOnImageCapturedCallback(captureCount = numImages)
             for (i in 0 until numImages) {
                 useCase.takePicture(mainExecutor, callback)
             }
 
             callback.awaitCapturesAndAssert(
-                timeout = numImages * CAPTURE_TIMEOUT,
+                timeout = CAPTURE_TIMEOUT.times(numImages),
                 capturedImagesCount = numImages
             )
         }
@@ -401,12 +403,12 @@
         }
 
         // Act.
-        val callback = FakeImageCaptureCallback(captureCount = numImages)
+        val callback = FakeOnImageCapturedCallback(captureCount = numImages)
         repeat(numImages) { useCase.takePicture(mainExecutor, callback) }
 
         // Assert.
         callback.awaitCapturesAndAssert(
-            timeout = numImages * CAPTURE_TIMEOUT,
+            timeout = CAPTURE_TIMEOUT.times(numImages),
             capturedImagesCount = numImages
         )
     }
@@ -463,7 +465,7 @@
         }
 
         // Act.
-        val callback = FakeImageCaptureCallback()
+        val callback = FakeOnImageCapturedCallback()
         imageCapture.takePicture(mainExecutor, callback)
 
         // Assert.
@@ -823,7 +825,7 @@
 
         // Wait for the signal that all the images have been saved.
         callback.awaitCapturesAndAssert(
-            timeout = numImages * CAPTURE_TIMEOUT,
+            timeout = CAPTURE_TIMEOUT.times(numImages),
             savedImagesCount = numImages
         )
     }
@@ -882,7 +884,7 @@
             cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
 
         useCase.takePicture(mainExecutor, callback)
 
@@ -913,14 +915,14 @@
             cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
 
         useCase.takePicture(mainExecutor, callback)
 
         // Wait for the signal that the image has been captured.
         callback.awaitCapturesAndAssert(capturedImagesCount = 1)
 
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
         assertThat(imageProperties.format).isEqualTo(ImageFormat.RAW10)
     }
 
@@ -1037,13 +1039,13 @@
         // directly know  onStateAttached() callback has been received. Therefore, taking a
         // picture and waiting for the capture success callback to know the use case's
         // onStateAttached() callback has been received.
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
         imageCapture.takePicture(mainExecutor, callback)
 
         // Wait for the signal that the image has been captured.
         callback.awaitCapturesAndAssert(capturedImagesCount = 1)
 
-        val callback2 = FakeImageCaptureCallback(captureCount = 3)
+        val callback2 = FakeOnImageCapturedCallback(captureCount = 3)
         imageCapture.takePicture(mainExecutor, callback2)
         imageCapture.takePicture(mainExecutor, callback2)
         imageCapture.takePicture(mainExecutor, callback2)
@@ -1066,7 +1068,7 @@
             cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, imageCapture)
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 3)
+        val callback = FakeOnImageCapturedCallback(captureCount = 3)
         imageCapture.takePicture(mainExecutor, callback)
         imageCapture.takePicture(mainExecutor, callback)
         imageCapture.takePicture(mainExecutor, callback)
@@ -1094,7 +1096,7 @@
     @Test
     fun takePictureReturnsErrorNO_CAMERA_whenNotBound() = runBlocking {
         val imageCapture = ImageCapture.Builder().build()
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
 
         imageCapture.takePicture(mainExecutor, callback)
 
@@ -1243,7 +1245,7 @@
             cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
         useCase.takePicture(mainExecutor, callback)
 
         // Wait for the signal that the image has been captured.
@@ -1253,7 +1255,7 @@
         // same as original one.
         val expectedCroppingRatio = Rational(DEFAULT_RESOLUTION.width, DEFAULT_RESOLUTION.height)
 
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
         val cropRect = imageProperties.cropRect
 
         // Rotate the captured ImageProxy's crop rect into the coordinate space of the final
@@ -1280,7 +1282,7 @@
             cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
 
         // Checks camera device sensor degrees to set target cropping aspect ratio match the
         // sensor orientation.
@@ -1298,7 +1300,7 @@
 
         // After target rotation is updated, the result cropping aspect ratio should still the
         // same as original one.
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
         val cropRect = imageProperties.cropRect
 
         // Rotate the captured ImageProxy's crop rect into the coordinate space of the final
@@ -1353,7 +1355,7 @@
             cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCaseGroup)
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
 
         useCase.takePicture(mainExecutor, callback)
 
@@ -1362,7 +1364,7 @@
 
         // After target rotation is updated, the result cropping aspect ratio should still the
         // same as original one.
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
         val cropRect = imageProperties.cropRect
 
         // Rotate the captured ImageProxy's crop rect into the coordinate space of the final
@@ -1447,13 +1449,13 @@
             cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
         useCase.takePicture(mainExecutor, callback)
 
         // Wait for the signal that the image has been captured.
         callback.awaitCapturesAndAssert(capturedImagesCount = 1)
 
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
         val cropRect = imageProperties.cropRect
         val cropRectAspectRatio = Rational(cropRect!!.height(), cropRect.width())
 
@@ -1610,13 +1612,13 @@
                 )
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
         useCase.takePicture(mainExecutor, callback)
 
         // Wait for the signal that the image has been captured.
         callback.awaitCapturesAndAssert(capturedImagesCount = 1)
 
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
 
         // Check the output image rotation degrees value is correct.
         assertThat(imageProperties.rotationDegrees)
@@ -1683,13 +1685,13 @@
                 )
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
         useCase.takePicture(mainExecutor, callback)
 
         // Wait for the signal that the image has been captured.
         callback.awaitCapturesAndAssert(capturedImagesCount = 1)
 
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
         // Check the output image rotation degrees value is correct.
         assertThat(imageProperties.rotationDegrees)
             .isEqualTo(camera.cameraInfo.getSensorRotationDegrees(useCase.targetRotation))
@@ -1721,13 +1723,13 @@
                 )
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
         useCase.takePicture(mainExecutor, callback)
 
         // Wait for the signal that the image has been captured.
         callback.awaitCapturesAndAssert(capturedImagesCount = 1)
 
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
 
         // Check the output image rotation degrees value is correct.
         assertThat(imageProperties.rotationDegrees)
@@ -1771,13 +1773,13 @@
                 )
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
         imageCapture.takePicture(mainExecutor, callback)
 
         // Wait for the signal that the image has been captured.
         callback.awaitCapturesAndAssert(capturedImagesCount = 1)
 
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
 
         // Check the output image rotation degrees value is correct.
         assertThat(imageProperties.rotationDegrees)
@@ -1819,13 +1821,13 @@
             )
         }
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
         imageCapture.takePicture(mainExecutor, callback)
 
         // Wait for the signal that the image has been captured.
         callback.awaitCapturesAndAssert(capturedImagesCount = 1)
 
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
 
         // Check the output image rotation degrees value is correct.
         if (isRotationOptionSupportedDevice()) {
@@ -1981,7 +1983,7 @@
             .isTrue()
 
         // Act.
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
         withContext(Dispatchers.Main) {
             // Test the reproduce step in b/235119898
             cameraProvider.unbind(preview)
@@ -2008,7 +2010,7 @@
         }
 
         // wait for camera to start by taking a picture
-        val callback1 = FakeImageCaptureCallback(captureCount = 1)
+        val callback1 = FakeOnImageCapturedCallback(captureCount = 1)
         imageCapture.takePicture(mainExecutor, callback1)
         try {
             callback1.awaitCapturesAndAssert(capturedImagesCount = 1)
@@ -2017,7 +2019,7 @@
         }
 
         // Act.
-        val callback2 = FakeImageCaptureCallback(captureCount = 1)
+        val callback2 = FakeOnImageCapturedCallback(captureCount = 1)
         withContext(Dispatchers.Main) {
             cameraProvider.unbind(videoCapture)
             imageCapture.takePicture(mainExecutor, callback2)
@@ -2169,13 +2171,13 @@
 
         assertThat(imageCapture.resolutionInfo!!.resolution).isEqualTo(maxHighResolutionOutputSize)
 
-        val callback = FakeImageCaptureCallback(captureCount = 1)
+        val callback = FakeOnImageCapturedCallback(captureCount = 1)
         imageCapture.takePicture(mainExecutor, callback)
 
         // Wait for the signal that the image has been captured.
         callback.awaitCapturesAndAssert(capturedImagesCount = 1)
 
-        val imageProperties = callback.results.first()
+        val imageProperties = callback.results.first().properties
         assertThat(imageProperties.size).isEqualTo(maxHighResolutionOutputSize)
     }
 
@@ -2313,7 +2315,7 @@
         }
 
         suspend fun awaitCapturesAndAssert(
-            timeout: Long = CAPTURE_TIMEOUT,
+            timeout: Duration = CAPTURE_TIMEOUT,
             savedImagesCount: Int = 0,
             errorsCount: Int = 0
         ) {
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/fakecamera/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/fakecamera/ImageCaptureTest.kt
new file mode 100644
index 0000000..af52c44
--- /dev/null
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/fakecamera/ImageCaptureTest.kt
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2024 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.integration.core.fakecamera
+
+import android.content.ContentValues
+import android.content.Context
+import android.os.Build
+import android.provider.MediaStore
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.testing.fakes.FakeAppConfig
+import androidx.camera.testing.fakes.FakeCamera
+import androidx.camera.testing.fakes.FakeCameraControl
+import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
+import androidx.camera.testing.impl.fakes.FakeOnImageCapturedCallback
+import androidx.camera.testing.impl.fakes.FakeOnImageSavedCallback
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import java.text.SimpleDateFormat
+import java.util.Locale
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Tests using a fake camera instead of real camera by replacing the camera-camera2 layer with
+ * camera-testing layer.
+ *
+ * They are aimed to ensure that integration between camera-core and camera-testing work seamlessly.
+ */
+@RunWith(Parameterized::class)
+class ImageCaptureTest(
+    @CameraSelector.LensFacing private val lensFacing: Int,
+) {
+    @get:Rule
+    val temporaryFolder =
+        TemporaryFolder(ApplicationProvider.getApplicationContext<Context>().cacheDir)
+
+    private val context = ApplicationProvider.getApplicationContext<Context>()
+    private lateinit var cameraProvider: ProcessCameraProvider
+    private lateinit var camera: FakeCamera
+    private lateinit var cameraControl: FakeCameraControl
+    private lateinit var imageCapture: ImageCapture
+
+    @Before
+    fun setup() = runBlocking {
+        cameraProvider = getFakeConfigCameraProvider(context)
+        imageCapture = bindImageCapture()
+    }
+
+    @After
+    fun tearDown() = runBlocking {
+        if (::cameraProvider.isInitialized) {
+            withContext(Dispatchers.Main) { cameraProvider.shutdownAsync()[10, TimeUnit.SECONDS] }
+        }
+    }
+
+    // Duplicate to ImageCaptureTest on core-test-app JVM tests, any change here may need to be
+    // reflected there too
+    @Test
+    fun canSubmitTakePictureRequest(): Unit = runBlocking {
+        val countDownLatch = CountDownLatch(1)
+        cameraControl.setOnNewCaptureRequestListener { countDownLatch.countDown() }
+
+        imageCapture.takePicture(CameraXExecutors.directExecutor(), FakeOnImageCapturedCallback())
+
+        assertThat(countDownLatch.await(3, TimeUnit.SECONDS)).isTrue()
+    }
+
+    // Duplicate to ImageCaptureTest on core-test-app JVM tests, any change here may need to be
+    // reflected there too
+    @Ignore("b/318314454")
+    @Test
+    fun canCreateBitmapFromTakenImage_whenImageCapturedCallbackIsUsed(): Unit = runBlocking {
+        val callback = FakeOnImageCapturedCallback()
+        imageCapture.takePicture(CameraXExecutors.directExecutor(), callback)
+        callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+        callback.results.first().image.toBitmap()
+    }
+
+    // Duplicate to ImageCaptureTest on core-test-app JVM tests, any change here may need to be
+    // reflected there too
+    @Ignore("b/318314454")
+    @Test
+    fun canFindImage_whenFileStorageAndImageSavedCallbackIsUsed(): Unit = runBlocking {
+        val saveLocation = temporaryFolder.newFile()
+        val previousLength = saveLocation.length()
+        val callback = FakeOnImageSavedCallback()
+
+        imageCapture.takePicture(
+            ImageCapture.OutputFileOptions.Builder(saveLocation).build(),
+            CameraXExecutors.directExecutor(),
+            callback
+        )
+
+        callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+        assertThat(saveLocation.length()).isGreaterThan(previousLength)
+    }
+
+    // Duplicate to ImageCaptureTest on androidTest/fakecamera/ImageCaptureTest, any change here may
+    // need to be reflected there too
+    @Ignore("b/318314454")
+    @Test
+    fun canFindImage_whenMediaStoreAndImageSavedCallbackIsUsed(): Unit = runBlocking {
+        val initialCount = getMediaStoreCameraXImageCount()
+        val callback = FakeOnImageSavedCallback()
+        imageCapture.takePicture(
+            createMediaStoreOutputOptions(),
+            CameraXExecutors.directExecutor(),
+            callback
+        )
+        callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+        assertThat(getMediaStoreCameraXImageCount()).isEqualTo(initialCount + 1)
+    }
+
+    private suspend fun bindImageCapture(): ImageCapture {
+        val imageCapture = ImageCapture.Builder().build()
+
+        withContext(Dispatchers.Main) {
+            cameraProvider.bindToLifecycle(
+                FakeLifecycleOwner().apply { startAndResume() },
+                CameraSelector.Builder().requireLensFacing(lensFacing).build(),
+                imageCapture
+            )
+        }
+
+        camera =
+            when (lensFacing) {
+                CameraSelector.LENS_FACING_BACK -> FakeAppConfig.getBackCamera()
+                CameraSelector.LENS_FACING_FRONT -> FakeAppConfig.getFrontCamera()
+                else -> throw AssertionError("Unsupported lens facing: $lensFacing")
+            }
+        cameraControl = camera.cameraControl as FakeCameraControl
+
+        return imageCapture
+    }
+
+    private fun getFakeConfigCameraProvider(context: Context): ProcessCameraProvider {
+        var cameraProvider: ProcessCameraProvider? = null
+        val latch = CountDownLatch(1)
+        ProcessCameraProvider.configureInstance(FakeAppConfig.create())
+        ProcessCameraProvider.getInstance(context)
+            .addListener(
+                {
+                    cameraProvider = ProcessCameraProvider.getInstance(context).get()
+                    latch.countDown()
+                },
+                CameraXExecutors.directExecutor()
+            )
+
+        Truth.assertWithMessage("ProcessCameraProvider.getInstance timed out!")
+            .that(latch.await(5, TimeUnit.SECONDS))
+            .isTrue()
+
+        return cameraProvider!!
+    }
+
+    private fun createMediaStoreOutputOptions(): ImageCapture.OutputFileOptions {
+        // Create time stamped name and MediaStore entry.
+        val name =
+            FILENAME_PREFIX +
+                SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.US)
+                    .format(System.currentTimeMillis())
+        val contentValues =
+            ContentValues().apply {
+                put(MediaStore.MediaColumns.DISPLAY_NAME, name)
+                put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
+                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
+                    put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
+                }
+            }
+
+        // Create output options object which contains file + metadata
+        return ImageCapture.OutputFileOptions.Builder(
+                context.contentResolver,
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+                contentValues
+            )
+            .build()
+    }
+
+    private fun getMediaStoreCameraXImageCount(): Int {
+        val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME)
+        val selection = "${MediaStore.Images.Media.DISPLAY_NAME} LIKE ?"
+        val selectionArgs = arrayOf("$FILENAME_PREFIX%")
+
+        val query =
+            ApplicationProvider.getApplicationContext<Context>()
+                .contentResolver
+                .query(
+                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+                    projection,
+                    selection,
+                    selectionArgs,
+                    null
+                )
+
+        return query?.use { cursor -> cursor.count } ?: 0
+    }
+
+    companion object {
+        private const val FILENAME_PREFIX = "cameraXPhoto"
+
+        @JvmStatic
+        @Parameterized.Parameters(name = "LensFacing = {0}")
+        fun data() =
+            listOf(
+                arrayOf(CameraSelector.LENS_FACING_BACK),
+                arrayOf(CameraSelector.LENS_FACING_FRONT),
+            )
+    }
+}
diff --git a/camera/integration-tests/coretestapp/src/test/java/androidx/camera/integration/core/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/test/java/androidx/camera/integration/core/ImageCaptureTest.kt
index b09987b..459187c 100644
--- a/camera/integration-tests/coretestapp/src/test/java/androidx/camera/integration/core/ImageCaptureTest.kt
+++ b/camera/integration-tests/coretestapp/src/test/java/androidx/camera/integration/core/ImageCaptureTest.kt
@@ -16,9 +16,10 @@
 
 package androidx.camera.integration.core
 
+import android.content.ContentValues
 import android.content.Context
 import android.os.Build
-import android.os.Looper
+import android.provider.MediaStore
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.ImageCapture
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
@@ -27,13 +28,17 @@
 import androidx.camera.testing.fakes.FakeAppConfig
 import androidx.camera.testing.fakes.FakeCamera
 import androidx.camera.testing.fakes.FakeCameraControl
-import androidx.camera.testing.impl.fakes.FakeImageCaptureCallback
 import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
+import androidx.camera.testing.impl.fakes.FakeOnImageCapturedCallback
+import androidx.camera.testing.impl.fakes.FakeOnImageSavedCallback
 import androidx.test.core.app.ApplicationProvider
 import androidx.testutils.MainDispatcherRule
 import com.google.common.truth.Truth.assertThat
+import java.text.SimpleDateFormat
+import java.util.Locale
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
@@ -42,9 +47,9 @@
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
+import org.junit.rules.TemporaryFolder
 import org.junit.runner.RunWith
 import org.robolectric.ParameterizedRobolectricTestRunner
-import org.robolectric.Shadows.shadowOf
 import org.robolectric.annotation.Config
 import org.robolectric.annotation.internal.DoNotInstrument
 
@@ -59,26 +64,20 @@
 
     @get:Rule val mainDispatcherRule = MainDispatcherRule(testDispatcher)
 
+    @get:Rule
+    val temporaryFolder =
+        TemporaryFolder(ApplicationProvider.getApplicationContext<Context>().cacheDir)
+
     private val context = ApplicationProvider.getApplicationContext<Context>()
     private lateinit var cameraProvider: ProcessCameraProvider
     private lateinit var camera: FakeCamera
     private lateinit var cameraControl: FakeCameraControl
     private lateinit var imageCapture: ImageCapture
 
-    companion object {
-        @JvmStatic
-        @ParameterizedRobolectricTestRunner.Parameters(name = "LensFacing = {0}")
-        fun data() =
-            listOf(
-                arrayOf(CameraSelector.LENS_FACING_BACK),
-                arrayOf(CameraSelector.LENS_FACING_FRONT),
-            )
-    }
-
     @Before
     fun setup() {
+        cameraProvider = getFakeConfigCameraProvider(context)
         imageCapture = bindImageCapture()
-        assertThat(imageCapture).isNotNull()
     }
 
     @After
@@ -88,28 +87,64 @@
         }
     }
 
+    // Duplicate to ImageCaptureTest on androidTest/fakecamera/ImageCaptureTest, any change here may
+    // need to be reflected there too
     @Test
     fun canSubmitTakePictureRequest(): Unit = runTest {
         val countDownLatch = CountDownLatch(1)
         cameraControl.setOnNewCaptureRequestListener { countDownLatch.countDown() }
 
-        imageCapture.takePicture(CameraXExecutors.directExecutor(), FakeImageCaptureCallback())
+        imageCapture.takePicture(CameraXExecutors.directExecutor(), FakeOnImageCapturedCallback())
 
         assertThat(countDownLatch.await(3, TimeUnit.SECONDS)).isTrue()
     }
 
-    @Ignore("TODO: b/318314454")
+    // Duplicate to ImageCaptureTest on androidTest/fakecamera/ImageCaptureTest, any change here may
+    // need to be reflected there too
+    @Ignore("b/318314454")
     @Test
-    fun canTakeImage(): Unit = runTest {
-        val callback = FakeImageCaptureCallback()
+    fun canCreateBitmapFromTakenImage_whenImageCapturedCallbackIsUsed(): Unit = runTest {
+        val callback = FakeOnImageCapturedCallback()
         imageCapture.takePicture(CameraXExecutors.directExecutor(), callback)
-        shadowOf(Looper.getMainLooper()).idle()
-        callback.awaitCaptures()
+        callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+        callback.results.first().image.toBitmap()
+    }
+
+    // Duplicate to ImageCaptureTest on androidTest/fakecamera/ImageCaptureTest, any change here may
+    // need to be reflected there too
+    @Ignore("b/318314454")
+    @Test
+    fun canFindImage_whenFileStorageAndImageSavedCallbackIsUsed(): Unit = runTest {
+        val saveLocation = temporaryFolder.newFile()
+        val previousLength = saveLocation.length()
+        val callback = FakeOnImageSavedCallback()
+
+        imageCapture.takePicture(
+            ImageCapture.OutputFileOptions.Builder(saveLocation).build(),
+            CameraXExecutors.directExecutor(),
+            callback
+        )
+
+        callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+        assertThat(saveLocation.length()).isGreaterThan(previousLength)
+    }
+
+    // Duplicate to ImageCaptureTest on androidTest/fakecamera/ImageCaptureTest, any change here may
+    // need to be reflected there too
+    @Ignore("b/318314454")
+    @Test
+    fun canFindFakeImageUri_whenMediaStoreAndImageSavedCallbackIsUsed(): Unit = runBlocking {
+        val callback = FakeOnImageSavedCallback()
+        imageCapture.takePicture(
+            createMediaStoreOutputOptions(),
+            CameraXExecutors.directExecutor(),
+            callback
+        )
+        callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+        assertThat(callback.results.first().savedUri).isNotNull()
     }
 
     private fun bindImageCapture(): ImageCapture {
-        cameraProvider = getFakeConfigCameraProvider(context)
-
         val imageCapture = ImageCapture.Builder().build()
 
         cameraProvider.bindToLifecycle(
@@ -127,4 +162,40 @@
 
         return imageCapture
     }
+
+    private fun createMediaStoreOutputOptions(): ImageCapture.OutputFileOptions {
+        // Create time stamped name and MediaStore entry.
+        val name =
+            FILENAME_PREFIX +
+                SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.US)
+                    .format(System.currentTimeMillis())
+        val contentValues =
+            ContentValues().apply {
+                put(MediaStore.MediaColumns.DISPLAY_NAME, name)
+                put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
+                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
+                    put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
+                }
+            }
+
+        // Create output options object which contains file + metadata
+        return ImageCapture.OutputFileOptions.Builder(
+                context.contentResolver,
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+                contentValues
+            )
+            .build()
+    }
+
+    companion object {
+        private const val FILENAME_PREFIX = "cameraXPhoto"
+
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "LensFacing = {0}")
+        fun data() =
+            listOf(
+                arrayOf(CameraSelector.LENS_FACING_BACK),
+                arrayOf(CameraSelector.LENS_FACING_FRONT),
+            )
+    }
 }
diff --git a/camera/viewfinder/viewfinder-compose/src/main/java/androidx/camera/viewfinder/androidx-camera-viewfinder-viewfinder-compose-documentation.md b/camera/viewfinder/viewfinder-compose/src/main/java/androidx/camera/viewfinder/androidx-camera-viewfinder-viewfinder-compose-documentation.md
index 10d4659..18eb197 100644
--- a/camera/viewfinder/viewfinder-compose/src/main/java/androidx/camera/viewfinder/androidx-camera-viewfinder-viewfinder-compose-documentation.md
+++ b/camera/viewfinder/viewfinder-compose/src/main/java/androidx/camera/viewfinder/androidx-camera-viewfinder-viewfinder-compose-documentation.md
@@ -1,7 +1,7 @@
 # Module root
 
-CameraX ViewFinder Compose
+Camera Viewfinder Compose
 
 # Package androidx.camera.viewfinder.compose
 
-Library providing a composable ViewFinder
+Standalone Composable Viewfinder for Camera
diff --git a/camera/viewfinder/viewfinder-core/src/main/java/androidx/camera/viewfinder/androidx-camera-viewfinder-viewfinder-core-documentation.md b/camera/viewfinder/viewfinder-core/src/main/java/androidx/camera/viewfinder/androidx-camera-viewfinder-viewfinder-core-documentation.md
index fe8fd60..6f4318c 100644
--- a/camera/viewfinder/viewfinder-core/src/main/java/androidx/camera/viewfinder/androidx-camera-viewfinder-viewfinder-core-documentation.md
+++ b/camera/viewfinder/viewfinder-core/src/main/java/androidx/camera/viewfinder/androidx-camera-viewfinder-viewfinder-core-documentation.md
@@ -1,7 +1,7 @@
 # Module root
 
-CameraX ViewFinder Core
+Camera Viewfinder Core
 
 # Package androidx.camera.viewfinder
 
-Library providing core dependencies for ViewFinder
+Core dependencies for Viewfinder
diff --git a/collection/collection/bcv/native/current.txt b/collection/collection/bcv/native/current.txt
index 215b18d..c3aa7c4 100644
--- a/collection/collection/bcv/native/current.txt
+++ b/collection/collection/bcv/native/current.txt
@@ -981,8 +981,6 @@
 }
 
 sealed class <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/ScatterMap { // androidx.collection/ScatterMap|null[0]
-    constructor <init>() // androidx.collection/ScatterMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/ScatterMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/ScatterMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/ScatterMap.size|{}size[0]
@@ -1024,8 +1022,6 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/FloatObjectMap { // androidx.collection/FloatObjectMap|null[0]
-    constructor <init>() // androidx.collection/FloatObjectMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/FloatObjectMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/FloatObjectMap.size|{}size[0]
@@ -1067,8 +1063,6 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/IntObjectMap { // androidx.collection/IntObjectMap|null[0]
-    constructor <init>() // androidx.collection/IntObjectMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/IntObjectMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/IntObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/IntObjectMap.size|{}size[0]
@@ -1110,8 +1104,6 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/LongObjectMap { // androidx.collection/LongObjectMap|null[0]
-    constructor <init>() // androidx.collection/LongObjectMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/LongObjectMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/LongObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/LongObjectMap.size|{}size[0]
@@ -1153,8 +1145,6 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/ObjectFloatMap { // androidx.collection/ObjectFloatMap|null[0]
-    constructor <init>() // androidx.collection/ObjectFloatMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/ObjectFloatMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/ObjectFloatMap.size|{}size[0]
@@ -1197,8 +1187,6 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/ObjectIntMap { // androidx.collection/ObjectIntMap|null[0]
-    constructor <init>() // androidx.collection/ObjectIntMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/ObjectIntMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/ObjectIntMap.size|{}size[0]
@@ -1241,8 +1229,6 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/ObjectList { // androidx.collection/ObjectList|null[0]
-    constructor <init>(kotlin/Int) // androidx.collection/ObjectList.<init>|<init>(kotlin.Int){}[0]
-
     final val indices // androidx.collection/ObjectList.indices|{}indices[0]
         final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/ObjectList.indices.<get-indices>|<get-indices>(){}[0]
     final val lastIndex // androidx.collection/ObjectList.lastIndex|{}lastIndex[0]
@@ -1301,8 +1287,6 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/ObjectLongMap { // androidx.collection/ObjectLongMap|null[0]
-    constructor <init>() // androidx.collection/ObjectLongMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/ObjectLongMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/ObjectLongMap.size|{}size[0]
@@ -1345,8 +1329,6 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/OrderedScatterSet { // androidx.collection/OrderedScatterSet|null[0]
-    constructor <init>() // androidx.collection/OrderedScatterSet.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/OrderedScatterSet.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/OrderedScatterSet.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/OrderedScatterSet.size|{}size[0]
@@ -1396,8 +1378,6 @@
 }
 
 sealed class <#A: kotlin/Any?> androidx.collection/ScatterSet { // androidx.collection/ScatterSet|null[0]
-    constructor <init>() // androidx.collection/ScatterSet.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/ScatterSet.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/ScatterSet.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/ScatterSet.size|{}size[0]
@@ -1432,8 +1412,6 @@
 }
 
 sealed class androidx.collection/DoubleList { // androidx.collection/DoubleList|null[0]
-    constructor <init>(kotlin/Int) // androidx.collection/DoubleList.<init>|<init>(kotlin.Int){}[0]
-
     final val indices // androidx.collection/DoubleList.indices|{}indices[0]
         final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/DoubleList.indices.<get-indices>|<get-indices>(){}[0]
     final val lastIndex // androidx.collection/DoubleList.lastIndex|{}lastIndex[0]
@@ -1486,8 +1464,6 @@
 }
 
 sealed class androidx.collection/FloatFloatMap { // androidx.collection/FloatFloatMap|null[0]
-    constructor <init>() // androidx.collection/FloatFloatMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/FloatFloatMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/FloatFloatMap.size|{}size[0]
@@ -1530,8 +1506,6 @@
 }
 
 sealed class androidx.collection/FloatIntMap { // androidx.collection/FloatIntMap|null[0]
-    constructor <init>() // androidx.collection/FloatIntMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/FloatIntMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/FloatIntMap.size|{}size[0]
@@ -1574,8 +1548,6 @@
 }
 
 sealed class androidx.collection/FloatList { // androidx.collection/FloatList|null[0]
-    constructor <init>(kotlin/Int) // androidx.collection/FloatList.<init>|<init>(kotlin.Int){}[0]
-
     final val indices // androidx.collection/FloatList.indices|{}indices[0]
         final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/FloatList.indices.<get-indices>|<get-indices>(){}[0]
     final val lastIndex // androidx.collection/FloatList.lastIndex|{}lastIndex[0]
@@ -1628,8 +1600,6 @@
 }
 
 sealed class androidx.collection/FloatLongMap { // androidx.collection/FloatLongMap|null[0]
-    constructor <init>() // androidx.collection/FloatLongMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/FloatLongMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/FloatLongMap.size|{}size[0]
@@ -1672,8 +1642,6 @@
 }
 
 sealed class androidx.collection/FloatSet { // androidx.collection/FloatSet|null[0]
-    constructor <init>() // androidx.collection/FloatSet.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/FloatSet.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatSet.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/FloatSet.size|{}size[0]
@@ -1707,8 +1675,6 @@
 }
 
 sealed class androidx.collection/IntFloatMap { // androidx.collection/IntFloatMap|null[0]
-    constructor <init>() // androidx.collection/IntFloatMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/IntFloatMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/IntFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/IntFloatMap.size|{}size[0]
@@ -1751,8 +1717,6 @@
 }
 
 sealed class androidx.collection/IntIntMap { // androidx.collection/IntIntMap|null[0]
-    constructor <init>() // androidx.collection/IntIntMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/IntIntMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/IntIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/IntIntMap.size|{}size[0]
@@ -1795,8 +1759,6 @@
 }
 
 sealed class androidx.collection/IntList { // androidx.collection/IntList|null[0]
-    constructor <init>(kotlin/Int) // androidx.collection/IntList.<init>|<init>(kotlin.Int){}[0]
-
     final val indices // androidx.collection/IntList.indices|{}indices[0]
         final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/IntList.indices.<get-indices>|<get-indices>(){}[0]
     final val lastIndex // androidx.collection/IntList.lastIndex|{}lastIndex[0]
@@ -1849,8 +1811,6 @@
 }
 
 sealed class androidx.collection/IntLongMap { // androidx.collection/IntLongMap|null[0]
-    constructor <init>() // androidx.collection/IntLongMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/IntLongMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/IntLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/IntLongMap.size|{}size[0]
@@ -1893,8 +1853,6 @@
 }
 
 sealed class androidx.collection/IntSet { // androidx.collection/IntSet|null[0]
-    constructor <init>() // androidx.collection/IntSet.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/IntSet.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/IntSet.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/IntSet.size|{}size[0]
@@ -1928,8 +1886,6 @@
 }
 
 sealed class androidx.collection/LongFloatMap { // androidx.collection/LongFloatMap|null[0]
-    constructor <init>() // androidx.collection/LongFloatMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/LongFloatMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/LongFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/LongFloatMap.size|{}size[0]
@@ -1972,8 +1928,6 @@
 }
 
 sealed class androidx.collection/LongIntMap { // androidx.collection/LongIntMap|null[0]
-    constructor <init>() // androidx.collection/LongIntMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/LongIntMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/LongIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/LongIntMap.size|{}size[0]
@@ -2016,8 +1970,6 @@
 }
 
 sealed class androidx.collection/LongList { // androidx.collection/LongList|null[0]
-    constructor <init>(kotlin/Int) // androidx.collection/LongList.<init>|<init>(kotlin.Int){}[0]
-
     final val indices // androidx.collection/LongList.indices|{}indices[0]
         final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/LongList.indices.<get-indices>|<get-indices>(){}[0]
     final val lastIndex // androidx.collection/LongList.lastIndex|{}lastIndex[0]
@@ -2070,8 +2022,6 @@
 }
 
 sealed class androidx.collection/LongLongMap { // androidx.collection/LongLongMap|null[0]
-    constructor <init>() // androidx.collection/LongLongMap.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/LongLongMap.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/LongLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/LongLongMap.size|{}size[0]
@@ -2114,8 +2064,6 @@
 }
 
 sealed class androidx.collection/LongSet { // androidx.collection/LongSet|null[0]
-    constructor <init>() // androidx.collection/LongSet.<init>|<init>(){}[0]
-
     final val capacity // androidx.collection/LongSet.capacity|{}capacity[0]
         final fun <get-capacity>(): kotlin/Int // androidx.collection/LongSet.capacity.<get-capacity>|<get-capacity>(){}[0]
     final val size // androidx.collection/LongSet.size|{}size[0]
diff --git a/collection/collection/build.gradle b/collection/collection/build.gradle
index 7833819..ff66423 100644
--- a/collection/collection/build.gradle
+++ b/collection/collection/build.gradle
@@ -53,7 +53,7 @@
         commonMain {
             dependencies {
                 api(libs.kotlinStdlib)
-                api(project(":annotation:annotation"))
+                api("androidx.annotation:annotation:1.9.0-alpha02")
             }
         }
 
diff --git a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/MonoSplineTest.kt b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/MonoSplineTest.kt
index 65d9e6f..fa3f502 100644
--- a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/MonoSplineTest.kt
+++ b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/MonoSplineTest.kt
@@ -22,7 +22,6 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
-@OptIn(ExperimentalAnimationSpecApi::class)
 @RunWith(JUnit4::class)
 class MonoSplineTest {
     @Test
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/MonoSpline.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/MonoSpline.kt
index 508d41d..1ce3959 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/MonoSpline.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/MonoSpline.kt
@@ -16,8 +16,11 @@
 
 package androidx.compose.animation.core
 
+import androidx.compose.ui.util.fastCoerceIn
 import kotlin.math.hypot
 
+private const val MonoSplineIsExtrapolate = true
+
 /**
  * This performs a spline interpolation in multiple dimensions time is an array of all positions and
  * y is a list of arrays each with the values at each point
@@ -26,7 +29,6 @@
     private val timePoints: FloatArray
     private val values: Array<FloatArray>
     private val tangents: Array<FloatArray>
-    private val isExtrapolate = true
     private val slopeTemp: FloatArray
 
     init {
@@ -88,22 +90,20 @@
 
     /** get the value of the j'th spline at time t */
     fun getPos(t: Float, j: Int): Float {
+        val values = values
+        val tangents = tangents
         val n = timePoints.size
-        if (isExtrapolate) {
-            if (t <= timePoints[0]) {
-                return values[0][j] + (t - timePoints[0]) * getSlope(timePoints[0], j)
-            }
-            if (t >= timePoints[n - 1]) {
-                return values[n - 1][j] + (t - timePoints[n - 1]) * getSlope(timePoints[n - 1], j)
+        val index = if (t <= timePoints[0]) 0 else (if (t >= timePoints[n - 1]) n - 1 else -1)
+        if (MonoSplineIsExtrapolate) {
+            if (index != -1) {
+                return values[index][j] + (t - timePoints[index]) * getSlope(timePoints[index], j)
             }
         } else {
-            if (t <= timePoints[0]) {
-                return values[0][j]
-            }
-            if (t >= timePoints[n - 1]) {
-                return values[n - 1][j]
+            if (index != -1) {
+                return values[index][j]
             }
         }
+
         for (i in 0 until n - 1) {
             if (t == timePoints[i]) {
                 return values[i][j]
@@ -115,7 +115,7 @@
                 val y2 = values[i + 1][j]
                 val t1 = tangents[i][j]
                 val t2 = tangents[i + 1][j]
-                return interpolate(h, x, y1, y2, t1, t2)
+                return hermiteInterpolate(h, x, y1, y2, t1, t2)
             }
         }
         return 0.0f // should never reach here
@@ -129,40 +129,30 @@
     fun getPos(time: Float, v: AnimationVector, index: Int = 0) {
         val n = timePoints.size
         val dim = values[0].size
-        if (isExtrapolate) {
-            if (time <= timePoints[0]) {
-                getSlope(timePoints[0], slopeTemp)
+        val k = if (time <= timePoints[0]) 0 else (if (time >= timePoints[n - 1]) n - 1 else -1)
+        if (MonoSplineIsExtrapolate) {
+            if (k != -1) {
+                getSlope(timePoints[k], slopeTemp)
                 for (j in 0 until dim) {
-                    v[j] = values[0][j] + (time - timePoints[0]) * slopeTemp[j]
-                }
-                return
-            }
-            if (time >= timePoints[n - 1]) {
-                getSlope(timePoints[n - 1], slopeTemp)
-                for (j in 0 until dim) {
-                    v[j] = values[n - 1][j] + (time - timePoints[n - 1]) * slopeTemp[j]
+                    v[j] = values[k][j] + (time - timePoints[k]) * slopeTemp[j]
                 }
                 return
             }
         } else {
-            if (time <= timePoints[0]) {
+            if (k != -1) {
                 for (j in 0 until dim) {
-                    v[j] = values[0][j]
-                }
-                return
-            }
-            if (time >= timePoints[n - 1]) {
-                for (j in 0 until dim) {
-                    v[j] = values[n - 1][j]
+                    v[j] = values[k][j]
                 }
                 return
             }
         }
+
         for (i in index until n - 1) {
             if (time == timePoints[i]) {
                 for (j in 0 until dim) {
                     v[j] = values[i][j]
                 }
+                return
             }
             if (time < timePoints[i + 1]) {
                 val h = timePoints[i + 1] - timePoints[i]
@@ -172,7 +162,7 @@
                     val y2 = values[i + 1][j]
                     val t1 = tangents[i][j]
                     val t2 = tangents[i + 1][j]
-                    v[j] = interpolate(h, x, y1, y2, t1, t2)
+                    v[j] = hermiteInterpolate(h, x, y1, y2, t1, t2)
                 }
                 return
             }
@@ -180,15 +170,12 @@
     }
 
     /** Get the differential of the value at time fill an array of slopes for each spline */
-    fun getSlope(time: Float, v: FloatArray) {
-        var t = time
-        val n = timePoints.size
+    private fun getSlope(time: Float, v: FloatArray) {
         val dim = values[0].size
-        if (t <= timePoints[0]) {
-            t = timePoints[0]
-        } else if (t >= timePoints[n - 1]) {
-            t = timePoints[n - 1]
-        }
+        val n = timePoints.size
+        val t = time.fastCoerceIn(timePoints[0], timePoints[n - 1])
+
+        if (v.size < dim) return
         for (i in 0 until n - 1) {
             if (t <= timePoints[i + 1]) {
                 val h = timePoints[i + 1] - timePoints[i]
@@ -198,12 +185,11 @@
                     val y2 = values[i + 1][j]
                     val t1 = tangents[i][j]
                     val t2 = tangents[i + 1][j]
-                    v[j] = diff(h, x, y1, y2, t1, t2) / h
+                    v[j] = hermiteDifferential(h, x, y1, y2, t1, t2) / h
                 }
                 break
             }
         }
-        return
     }
 
     /**
@@ -213,78 +199,104 @@
      * You may provide [index] to simplify searching for the correct keyframe for the given [time].
      */
     fun getSlope(time: Float, v: AnimationVector, index: Int = 0) {
-        val t = time
+        val timePoints = timePoints
+        val values = values
+        val tangents = tangents
+
         val n = timePoints.size
         val dim = values[0].size
 
         // If time is 0, max or out of range we directly return the corresponding slope value
-        if (t <= timePoints[0]) {
+        val tangentIndex =
+            if (time <= timePoints[0]) 0 else (if (time >= timePoints[n - 1]) n - 1 else -1)
+        if (tangentIndex != -1) {
+            val tangent = tangents[tangentIndex]
+            // Help ART eliminate bound checks
+            if (tangent.size < dim) return
             for (j in 0 until dim) {
-                v[j] = tangents[0][j]
-            }
-            return
-        } else if (t >= timePoints[n - 1]) {
-            for (j in 0 until dim) {
-                v[j] = tangents[n - 1][j]
+                v[j] = tangent[j]
             }
             return
         }
 
         // Otherwise, calculate interpolated velocity
         for (i in index until n - 1) {
-            if (t <= timePoints[i + 1]) {
+            if (time <= timePoints[i + 1]) {
                 val h = timePoints[i + 1] - timePoints[i]
-                val x = (t - timePoints[i]) / h
+                val x = (time - timePoints[i]) / h
                 for (j in 0 until dim) {
                     val y1 = values[i][j]
                     val y2 = values[i + 1][j]
                     val t1 = tangents[i][j]
                     val t2 = tangents[i + 1][j]
-                    v[j] = diff(h, x, y1, y2, t1, t2) / h
+                    v[j] = hermiteDifferential(h, x, y1, y2, t1, t2) / h
                 }
                 break
             }
         }
-        return
     }
 
     private fun getSlope(time: Float, j: Int): Float {
-        var t = time
+        val timePoints = timePoints
+        val values = values
+        val tangents = tangents
         val n = timePoints.size
-        if (t < timePoints[0]) {
-            t = timePoints[0]
-        } else if (t >= timePoints[n - 1]) {
-            t = timePoints[n - 1]
-        }
+        val t = time.fastCoerceIn(timePoints[0], timePoints[n - 1])
         for (i in 0 until n - 1) {
             if (t <= timePoints[i + 1]) {
-                val h = timePoints[i + 1] - timePoints[i]
-                val x = (t - timePoints[i]) / h
                 val y1 = values[i][j]
                 val y2 = values[i + 1][j]
                 val t1 = tangents[i][j]
                 val t2 = tangents[i + 1][j]
-                return diff(h, x, y1, y2, t1, t2) / h
+                val h = timePoints[i + 1] - timePoints[i]
+                val x = (t - timePoints[i]) / h
+                return hermiteDifferential(h, x, y1, y2, t1, t2) / h
             }
         }
         return 0.0f // should never reach here
     }
+}
 
-    /** Cubic Hermite spline */
-    private fun interpolate(h: Float, x: Float, y1: Float, y2: Float, t1: Float, t2: Float): Float {
-        val x2 = x * x
-        val x3 = x2 * x
-        return (-2 * x3 * y2 + 3 * x2 * y2 + 2 * x3 * y1 - 3 * x2 * y1 +
-            y1 +
-            h * t2 * x3 +
-            h * t1 * x3 - h * t2 * x2 - 2 * h * t1 * x2 + h * t1 * x)
-    }
+/** Cubic Hermite spline */
+internal fun hermiteInterpolate(
+    h: Float,
+    x: Float,
+    y1: Float,
+    y2: Float,
+    t1: Float,
+    t2: Float
+): Float {
+    val x2 = x * x
+    val x3 = x2 * x
+    // The exact formula is as follows:
+    //
+    //     -2 * x3 * y2 + 3 * x2 * y2 + 2 * x3 * y1 - 3 * x2 * y1 +
+    //     y1 +
+    //     h * t2 * x3 +
+    //     h * t1 * x3 - h * t2 * x2 - 2 * h * t1 * x2 + h * t1 * x)
+    //
+    // The code below is equivalent but factored to go from 30 down to 20 instructions
+    // on aarch64 devices
+    return h * t1 * (x - 2 * x2 + x3) + h * t2 * (x3 - x2) + y1 - (3 * x2 - 2 * x3) * (y1 - y2)
+}
 
-    /** Cubic Hermite spline slope differentiated */
-    private fun diff(h: Float, x: Float, y1: Float, y2: Float, t1: Float, t2: Float): Float {
-        val x2 = x * x
-        return (-6 * x2 * y2 + 6 * x * y2 + 6 * x2 * y1 - 6 * x * y1 +
-            3 * h * t2 * x2 +
-            3 * h * t1 * x2 - 2 * h * t2 * x - 4 * h * t1 * x + h * t1)
-    }
+/** Cubic Hermite spline slope differentiated */
+internal fun hermiteDifferential(
+    h: Float,
+    x: Float,
+    y1: Float,
+    y2: Float,
+    t1: Float,
+    t2: Float
+): Float {
+    // The exact formula is as follows:
+    //
+    //    -6 * x2 * y2 + 6 * x * y2 + 6 * x2 * y1 - 6 * x * y1 +
+    //     3 * h * t2 * x2 +
+    //     3 * h * t1 * x2 - 2 * h * t2 * x - 4 * h * t1 * x + h * t1
+    //
+    // The code below is equivalent but factored to go from 33 down to 19 instructions
+    // on aarch64 devices
+    val x2 = x * x
+    return h * (t1 - 2 * x * (2 * t1 + t2) + 3 * (t1 + t2) * x2) - 6 * (x - x2) * (y1 - y2)
 }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
index 1fc3a21..9ceaf13 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
@@ -1416,7 +1416,7 @@
 
         init {
             val visibilityThreshold: T? =
-                visibilityThresholdMap.get(typeConverter)?.let {
+                VisibilityThresholdMap.get(typeConverter)?.let {
                     val vector = typeConverter.convertToVector(initialValue)
                     for (id in 0 until vector.size) {
                         vector[id] = it
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedMonoSplineKeyframesSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedMonoSplineKeyframesSpec.kt
index ddc9938..b92b34a 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedMonoSplineKeyframesSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedMonoSplineKeyframesSpec.kt
@@ -28,22 +28,22 @@
     private val periodicBias: Float,
 ) : VectorizedDurationBasedAnimationSpec<V> {
     // Objects initialized lazily once
-    private lateinit var valueVector: V
-    private lateinit var velocityVector: V
+    private var valueVector: V? = null
+    private var velocityVector: V? = null
 
     // Time values passed to MonoSpline.
     private lateinit var times: FloatArray
 
     // Objects for MonoSpline
-    private lateinit var monoSpline: MonoSpline
+    private var monoSpline: MonoSpline? = null
     // [values] are not modified by MonoSpline so we can safely re-use it to re-instantiate it
-    private lateinit var values: Array<FloatArray>
+    private var values: Array<FloatArray>? = null
     private var lastInitialValue: V? = null
     private var lastTargetValue: V? = null
 
     private fun init(initialValue: V, targetValue: V, initialVelocity: V) {
         // Only need to initialize once
-        if (!::valueVector.isInitialized) {
+        if (valueVector == null) {
             valueVector = initialValue.newInstance()
             velocityVector = initialVelocity.newInstance()
 
@@ -52,9 +52,7 @@
 
         // Need to re-initialize based on initial/target values
         if (
-            !::monoSpline.isInitialized ||
-                lastInitialValue != initialValue ||
-                lastTargetValue != targetValue
+            monoSpline == null || lastInitialValue != initialValue || lastTargetValue != targetValue
         ) {
             val initialChanged = lastInitialValue != initialValue
             val targetChanged = lastTargetValue != targetValue
@@ -63,39 +61,31 @@
 
             val dimension = initialValue.size
 
-            if (!::values.isInitialized) {
+            var values = values
+            if (values == null) {
                 values =
                     Array(timestamps.size) {
-                        when (val timestamp = timestamps[it]) {
-                            // Start (zero) and end (durationMillis) may not have been declared in
-                            // keyframes
-                            0 -> {
-                                if (!keyframes.contains(timestamp)) {
-                                    FloatArray(dimension, initialValue::get)
-                                } else {
-                                    FloatArray(dimension, keyframes[timestamp]!!.first::get)
-                                }
-                            }
-                            durationMillis -> {
-                                if (!keyframes.contains(timestamp)) {
-                                    FloatArray(dimension, targetValue::get)
-                                } else {
-                                    FloatArray(dimension, keyframes[timestamp]!!.first::get)
-                                }
-                            }
-
-                            // All other values are guaranteed to exist
-                            else -> FloatArray(dimension, keyframes[timestamp]!!.first::get)
+                        val timestamp = timestamps[it]
+                        val keyframe = keyframes[timestamp]
+                        if (timestamp == 0 && keyframe == null) {
+                            FloatArray(dimension) { i -> initialValue[i] }
+                        } else if (timestamp == durationMillis && keyframe == null) {
+                            FloatArray(dimension) { i -> targetValue[i] }
+                        } else {
+                            val vectorValue = keyframe!!.first
+                            FloatArray(dimension) { i -> vectorValue[i] }
                         }
                     }
+                this.values = values
             } else {
                 // We can re-use most of the objects. Only the start and end may need to be replaced
                 if (initialChanged && !keyframes.contains(0)) {
-                    values[timestamps.binarySearch(0)] = FloatArray(dimension, initialValue::get)
+                    val index = timestamps.binarySearch(0)
+                    values[index] = FloatArray(dimension) { i -> initialValue[i] }
                 }
                 if (targetChanged && !keyframes.contains(durationMillis)) {
-                    values[timestamps.binarySearch(durationMillis)] =
-                        FloatArray(dimension, targetValue::get)
+                    val index = timestamps.binarySearch(durationMillis)
+                    values[index] = FloatArray(dimension) { i -> targetValue[i] }
                 }
             }
             monoSpline = MonoSpline(times, values, periodicBias)
@@ -111,13 +101,16 @@
         val playTimeMillis = playTimeNanos / MillisToNanos
         val clampedPlayTime = clampPlayTime(playTimeMillis).toInt()
         // If there is a key frame defined with the given time stamp, return that value
-        if (keyframes.containsKey(clampedPlayTime)) {
-            return keyframes[clampedPlayTime]!!.first
+        val keyframe = keyframes[clampedPlayTime]
+        if (keyframe != null) {
+            return keyframe.first
         }
 
         if (clampedPlayTime >= durationMillis) {
             return targetValue
-        } else if (clampedPlayTime <= 0) return initialValue
+        } else if (clampedPlayTime <= 0) {
+            return initialValue
+        }
 
         init(initialValue, targetValue, initialVelocity)
 
@@ -125,7 +118,8 @@
         // time range at every call
         val index = findEntryForTimeMillis(clampedPlayTime)
 
-        monoSpline.getPos(
+        val valueVector = valueVector!!
+        monoSpline!!.getPos(
             index = index,
             time = getEasedTimeFromIndex(index, clampedPlayTime),
             v = valueVector
@@ -141,9 +135,6 @@
     ): V {
         val playTimeMillis = playTimeNanos / MillisToNanos
         val clampedPlayTime = clampPlayTime(playTimeMillis).toInt()
-        if (clampedPlayTime < 0) {
-            return initialVelocity
-        }
 
         init(initialValue, targetValue, initialVelocity)
 
@@ -151,7 +142,8 @@
         // time range at every call
         val index = findEntryForTimeMillis(clampedPlayTime)
 
-        monoSpline.getSlope(
+        val velocityVector = velocityVector!!
+        monoSpline!!.getSlope(
             index = index,
             time = getEasedTimeFromIndex(index, clampedPlayTime),
             v = velocityVector
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt
index 8852c65..6920792 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt
@@ -28,7 +28,7 @@
 private const val DpVisibilityThreshold = 0.1f
 private const val PxVisibilityThreshold = 0.5f
 
-private val rectVisibilityThreshold =
+private val RectVisibilityThreshold =
     Rect(PxVisibilityThreshold, PxVisibilityThreshold, PxVisibilityThreshold, PxVisibilityThreshold)
 
 /**
@@ -93,11 +93,14 @@
  * to stop when the value is close enough to the target.
  */
 public val Rect.Companion.VisibilityThreshold: Rect
-    get() = rectVisibilityThreshold
+    get() = RectVisibilityThreshold
 
 // TODO: Add Dp.DefaultAnimation = spring<Dp>(visibilityThreshold = Dp.VisibilityThreshold)
-
-internal val visibilityThresholdMap: Map<TwoWayConverter<*, *>, Float> =
+// The floats coming out of this map are fed to APIs that expect objects (generics), so it's
+// better to store them as boxed floats here instead of causing unboxing/boxing every time
+// the values are read out and forwarded to other APIs
+@Suppress("PrimitiveInCollection")
+internal val VisibilityThresholdMap: Map<TwoWayConverter<*, *>, Float> =
     mapOf(
         Int.VectorConverter to 1f,
         IntSize.VectorConverter to 1f,
diff --git a/compose/animation/animation/api/current.txt b/compose/animation/animation/api/current.txt
index e3b1efc..05c53bd 100644
--- a/compose/animation/animation/api/current.txt
+++ b/compose/animation/animation/api/current.txt
@@ -5,6 +5,10 @@
     method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> defaultDecayAnimationSpec();
   }
 
+  public final class AnimateBoundsModifierKt {
+    method @SuppressCompatibility @androidx.compose.animation.ExperimentalSharedTransitionApi public static androidx.compose.ui.Modifier animateBounds(androidx.compose.ui.Modifier, androidx.compose.ui.layout.LookaheadScope lookaheadScope, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.BoundsTransform boundsTransform, optional boolean animateMotionFrameOfReference);
+  }
+
   public final class AnimatedContentKt {
     method @androidx.compose.runtime.Composable public static <S> void AnimatedContent(androidx.compose.animation.core.Transition<S>, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<S>,androidx.compose.animation.ContentTransform> transitionSpec, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.jvm.functions.Function1<? super S,? extends java.lang.Object?> contentKey, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super S,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static <S> void AnimatedContent(S targetState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<S>,androidx.compose.animation.ContentTransform> transitionSpec, optional androidx.compose.ui.Alignment contentAlignment, optional String label, optional kotlin.jvm.functions.Function1<? super S,? extends java.lang.Object?> contentKey, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super S,kotlin.Unit> content);
diff --git a/compose/animation/animation/api/restricted_current.txt b/compose/animation/animation/api/restricted_current.txt
index e3b1efc..05c53bd 100644
--- a/compose/animation/animation/api/restricted_current.txt
+++ b/compose/animation/animation/api/restricted_current.txt
@@ -5,6 +5,10 @@
     method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> defaultDecayAnimationSpec();
   }
 
+  public final class AnimateBoundsModifierKt {
+    method @SuppressCompatibility @androidx.compose.animation.ExperimentalSharedTransitionApi public static androidx.compose.ui.Modifier animateBounds(androidx.compose.ui.Modifier, androidx.compose.ui.layout.LookaheadScope lookaheadScope, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.BoundsTransform boundsTransform, optional boolean animateMotionFrameOfReference);
+  }
+
   public final class AnimatedContentKt {
     method @androidx.compose.runtime.Composable public static <S> void AnimatedContent(androidx.compose.animation.core.Transition<S>, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<S>,androidx.compose.animation.ContentTransform> transitionSpec, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.jvm.functions.Function1<? super S,? extends java.lang.Object?> contentKey, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super S,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static <S> void AnimatedContent(S targetState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<S>,androidx.compose.animation.ContentTransform> transitionSpec, optional androidx.compose.ui.Alignment contentAlignment, optional String label, optional kotlin.jvm.functions.Function1<? super S,? extends java.lang.Object?> contentKey, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super S,kotlin.Unit> content);
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
index c8b339c..4f00d4f 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
@@ -38,7 +38,9 @@
 import androidx.compose.animation.demos.layoutanimation.ScreenTransitionDemo
 import androidx.compose.animation.demos.layoutanimation.ShrineCartDemo
 import androidx.compose.animation.demos.lookahead.AnimateBoundsModifierDemo
+import androidx.compose.animation.demos.lookahead.AnimateBoundsOnFloatingToolbarDemo
 import androidx.compose.animation.demos.lookahead.CraneDemo
+import androidx.compose.animation.demos.lookahead.LookaheadInScrollingColumn
 import androidx.compose.animation.demos.lookahead.LookaheadLayoutWithAlignmentLinesDemo
 import androidx.compose.animation.demos.lookahead.LookaheadSamplesDemo
 import androidx.compose.animation.demos.lookahead.LookaheadWithAnimatedContentSize
@@ -144,6 +146,10 @@
                     },
                     ComposableDemo("Lookahead With Tab Row") { LookaheadWithTabRowDemo() },
                     ComposableDemo("Lookahead With Scaffold") { LookaheadWithScaffold() },
+                    ComposableDemo("Lookahead With Scroll") { LookaheadInScrollingColumn() },
+                    ComposableDemo("Floating Toolbar w/ AnimateBounds") {
+                        AnimateBoundsOnFloatingToolbarDemo()
+                    },
                 )
             ),
             DemoCategory(
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifier.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifier.kt
deleted file mode 100644
index 87e9e5b..0000000
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifier.kt
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * 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.animation.demos.lookahead
-
-import androidx.compose.animation.core.AnimationVector2D
-import androidx.compose.animation.core.DeferredTargetAnimation
-import androidx.compose.animation.core.ExperimentalAnimatableApi
-import androidx.compose.animation.core.FiniteAnimationSpec
-import androidx.compose.animation.core.Spring
-import androidx.compose.animation.core.VectorConverter
-import androidx.compose.animation.core.spring
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-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.layout.LookaheadScope
-import androidx.compose.ui.layout.Placeable
-import androidx.compose.ui.layout.approachLayout
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.round
-import kotlinx.coroutines.CoroutineScope
-
-context(LookaheadScope)
-@OptIn(ExperimentalAnimatableApi::class)
-fun Modifier.animateBounds(
-    modifier: Modifier = Modifier,
-    sizeAnimationSpec: FiniteAnimationSpec<IntSize> =
-        spring(Spring.DampingRatioNoBouncy, Spring.StiffnessMediumLow),
-    positionAnimationSpec: FiniteAnimationSpec<IntOffset> =
-        spring(Spring.DampingRatioNoBouncy, Spring.StiffnessMediumLow),
-    debug: Boolean = false,
-) = composed {
-    val outerOffsetAnimation = remember { DeferredTargetAnimation(IntOffset.VectorConverter) }
-    val outerSizeAnimation = remember { DeferredTargetAnimation(IntSize.VectorConverter) }
-
-    val offsetAnimation = remember { DeferredTargetAnimation(IntOffset.VectorConverter) }
-    val sizeAnimation = remember { DeferredTargetAnimation(IntSize.VectorConverter) }
-
-    val coroutineScope = rememberCoroutineScope()
-
-    // The measure logic in `approachLayout` is skipped in the lookahead pass, as
-    // approachLayout is expected to produce intermediate stages of a layout transform.
-    // When the measure block is invoked after lookahead pass, the lookahead size of the
-    // child will be accessible as a parameter to the measure block.
-    this.drawWithContent {
-            drawContent()
-            if (debug) {
-                //                val offset = outerOffsetAnimation.pendingTarget!! -
-                // outerOffsetAnimation.value!!
-                //                translate(
-                //                    offset.x.toFloat(), offset.y.toFloat()
-                //                ) {
-                //                    drawRect(Color.Black.copy(alpha = 0.5f), style = Stroke(10f))
-                //                }
-            }
-        }
-        .approachLayout(
-            isMeasurementApproachInProgress = {
-                outerSizeAnimation.updateTarget(it, coroutineScope, sizeAnimationSpec)
-                !outerSizeAnimation.isIdle
-            },
-            isPlacementApproachInProgress = {
-                val target = lookaheadScopeCoordinates.localLookaheadPositionOf(it)
-                outerOffsetAnimation.updateTarget(
-                    target.round(),
-                    coroutineScope,
-                    positionAnimationSpec
-                )
-                !outerOffsetAnimation.isIdle
-            }
-        ) { measurable, constraints ->
-            val (w, h) =
-                outerSizeAnimation.updateTarget(
-                    lookaheadSize,
-                    coroutineScope,
-                    sizeAnimationSpec,
-                )
-            measurable.measure(constraints).run {
-                layout(w, h) {
-                    with(coroutineScope) {
-                        val (x, y) =
-                            outerOffsetAnimation.updateTargetBasedOnCoordinates(
-                                positionAnimationSpec
-                            )
-                        place(x, y)
-                    }
-                }
-            }
-        }
-        .then(modifier)
-        .drawWithContent {
-            drawContent()
-            if (debug) {
-                //                val offset = offsetAnimation.pendingTarget!! -
-                // offsetAnimation.value!!
-                //                translate(
-                //                    offset.x.toFloat(), offset.y.toFloat()
-                //                ) {
-                //                    drawRect(Color.Green.copy(alpha = 0.5f), style = Stroke(10f))
-                //                }
-            }
-        }
-        .approachLayout(
-            isMeasurementApproachInProgress = {
-                sizeAnimation.updateTarget(it, coroutineScope, sizeAnimationSpec)
-                !sizeAnimation.isIdle
-            },
-            isPlacementApproachInProgress = {
-                val target = lookaheadScopeCoordinates.localLookaheadPositionOf(it)
-                offsetAnimation.updateTarget(target.round(), coroutineScope, positionAnimationSpec)
-                !offsetAnimation.isIdle
-            }
-        ) { measurable, _ ->
-            // When layout changes, the lookahead pass will calculate a new final size for the
-            // child modifier. This lookahead size can be used to animate the size
-            // change, such that the animation starts from the current size and gradually
-            // change towards `lookaheadSize`.
-            val (width, height) =
-                sizeAnimation.updateTarget(
-                    lookaheadSize,
-                    coroutineScope,
-                    sizeAnimationSpec,
-                )
-            // Creates a fixed set of constraints using the animated size
-            val animatedConstraints = Constraints.fixed(width, height)
-            // Measure child/children with animated constraints.
-            val placeable = measurable.measure(animatedConstraints)
-            layout(placeable.width, placeable.height) {
-                val (x, y) =
-                    with(coroutineScope) {
-                        offsetAnimation.updateTargetBasedOnCoordinates(
-                            positionAnimationSpec,
-                        )
-                    }
-                placeable.place(x, y)
-            }
-        }
-}
-
-context(LookaheadScope, Placeable.PlacementScope, CoroutineScope)
-@OptIn(ExperimentalAnimatableApi::class)
-internal fun DeferredTargetAnimation<IntOffset, AnimationVector2D>.updateTargetBasedOnCoordinates(
-    animationSpec: FiniteAnimationSpec<IntOffset>,
-): IntOffset {
-    coordinates?.let { coordinates ->
-        with(this@PlacementScope) {
-            val targetOffset = lookaheadScopeCoordinates.localLookaheadPositionOf(coordinates)
-            val animOffset =
-                updateTarget(
-                    targetOffset.round(),
-                    this@CoroutineScope,
-                    animationSpec,
-                )
-            val current =
-                lookaheadScopeCoordinates.localPositionOf(coordinates, Offset.Zero).round()
-            return (animOffset - current)
-        }
-    }
-
-    return IntOffset.Zero
-}
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifierDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifierDemo.kt
index 5ebb842..787045d 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifierDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifierDemo.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Box
@@ -39,6 +41,7 @@
 import androidx.compose.ui.unit.dp
 import kotlin.random.Random
 
+@OptIn(ExperimentalSharedTransitionApi::class)
 @Composable
 fun AnimateBoundsModifierDemo() {
     var height by remember { mutableIntStateOf(200) }
@@ -63,18 +66,27 @@
             Box(Modifier.fillMaxHeight(0.5f).fillMaxSize()) {
                 Box(
                     Modifier.background(Color.Gray)
-                        .animateBounds(Modifier.padding(left.dp, top.dp, right.dp, bottom.dp))
+                        .animateBounds(
+                            this@LookaheadScope,
+                            Modifier.padding(left.dp, top.dp, right.dp, bottom.dp)
+                        )
                         .background(Color.Red)
                         .fillMaxSize()
                 )
             }
             Row(Modifier.fillMaxSize(), verticalAlignment = Alignment.CenterVertically) {
                 Box(
-                    Modifier.animateBounds(Modifier.weight(weight).height(height.dp))
+                    Modifier.animateBounds(
+                            this@LookaheadScope,
+                            Modifier.weight(weight).height(height.dp)
+                        )
                         .background(Color(0xffa2d2ff), RoundedCornerShape(5.dp))
                 )
                 Box(
-                    Modifier.animateBounds(Modifier.weight(1f).height(height.dp))
+                    Modifier.animateBounds(
+                            this@LookaheadScope,
+                            Modifier.weight(1f).height(height.dp)
+                        )
                         .background(Color(0xfffff3b0))
                 )
             }
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsOnFloatingToolbar.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsOnFloatingToolbar.kt
new file mode 100644
index 0000000..0221499
--- /dev/null
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsOnFloatingToolbar.kt
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2024 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.animation.demos.lookahead
+
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.demos.visualaid.EasingItemDemo
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.Icon
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Edit
+import androidx.compose.material.icons.outlined.FavoriteBorder
+import androidx.compose.material.icons.outlined.Share
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentWithReceiverOf
+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.layout.LookaheadScope
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.coerceAtLeast
+import androidx.compose.ui.unit.dp
+
+/**
+ * Example using [animateBounds] with nested movable content.
+ *
+ * Animates an Icon component from a Toolbar to a FAB position, the toolbar is also animated to hide
+ * it under the FAB.
+ */
+@Preview
+@Composable
+fun AnimateBoundsOnFloatingToolbarDemo() {
+    Box(Modifier.fillMaxSize()) {
+        Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState())) {
+            val sampleText = remember { LoremIpsum().values.first() }
+            Text(
+                text = "Click on the Toolbar to animate",
+                modifier = Modifier.fillMaxWidth(),
+                textAlign = TextAlign.Center,
+                style = MaterialTheme.typography.h6
+            )
+            Text(text = sampleText)
+        }
+        FloatingFabToolbar(
+            Modifier.align(Alignment.BottomCenter)
+                .fillMaxWidth()
+                .padding(8.dp)
+                .padding(bottom = 24.dp)
+        )
+    }
+}
+
+@OptIn(ExperimentalSharedTransitionApi::class)
+@Composable
+private fun FloatingFabToolbar(modifier: Modifier = Modifier) {
+    var mode by remember { mutableStateOf(FabToolbarMode.Toolbar) }
+
+    val animationDuration = 600
+    val animEasing = EasingItemDemo.EmphasizedEasing.function
+
+    val editIconPadding by
+        animateDpAsState(
+            targetValue = if (mode == FabToolbarMode.Fab) 12.dp else 0.dp,
+            animationSpec = tween(animationDuration, easing = animEasing),
+            label = "Edit Icon Padding"
+        )
+
+    val myEditIcon = remember {
+        movableContentWithReceiverOf<LookaheadScope, Modifier> { iconModifier ->
+            Box(
+                modifier =
+                    iconModifier
+                        .let {
+                            if (mode == FabToolbarMode.Toolbar) {
+                                it.fillMaxSize()
+                            } else {
+                                it.aspectRatio(1f, matchHeightConstraintsFirst = true)
+                            }
+                        }
+                        .animateBounds(
+                            lookaheadScope = this,
+                            modifier = Modifier,
+                            boundsTransform = { _, _ ->
+                                tween(animationDuration, easing = animEasing)
+                            },
+                        ),
+            ) {
+                Icon(
+                    imageVector = Icons.Outlined.Edit,
+                    contentDescription = null,
+                    tint = MaterialTheme.colors.onPrimary,
+                    modifier =
+                        Modifier.background(MaterialTheme.colors.primary, RoundedCornerShape(16.dp))
+                            .fillMaxSize()
+                            .padding(editIconPadding.coerceAtLeast(0.dp))
+                )
+            }
+        }
+    }
+
+    // Toolbar container + Toolbar
+    val myToolbar = remember {
+        movableContentWithReceiverOf<LookaheadScope, Modifier> { toolbarMod ->
+            // Toolbar container
+            Box(
+                modifier =
+                    toolbarMod
+                        .animateBounds(
+                            lookaheadScope = this,
+                            boundsTransform = { _, _ ->
+                                tween(animationDuration, easing = animEasing)
+                            },
+                        )
+                        .background(MaterialTheme.colors.background, RoundedCornerShape(50))
+                        .let {
+                            if (mode == FabToolbarMode.Toolbar) {
+                                // Respect toolbar content size when in Toolbar mode
+                                it.wrapContentSize().padding(8.dp)
+                            } else {
+                                // Resize the container so that it doesn't go beyond the Fab box,
+                                // clipping the toolbar as needed
+                                it.fillMaxWidth().wrapContentHeight().padding(8.dp)
+                            }
+                        }
+            ) {
+                // Toolbar - Fixed Size
+                Row(
+                    modifier = Modifier.align(Alignment.Center),
+                    horizontalArrangement = Arrangement.spacedBy(26.dp),
+                    verticalAlignment = Alignment.CenterVertically
+                ) {
+                    val iconSize = DpSize(30.dp, 20.dp)
+                    Icon(
+                        imageVector = Icons.Outlined.Share,
+                        contentDescription = "Share",
+                        modifier = Modifier.size(iconSize)
+                    )
+                    Icon(
+                        imageVector = Icons.Outlined.FavoriteBorder,
+                        contentDescription = "Favorite",
+                        modifier = Modifier.size(iconSize)
+                    )
+                    Box(modifier = Modifier.size(iconSize)) {
+                        // Slot for the Edit Icon when position on the toolbar
+                        if (mode == FabToolbarMode.Toolbar) {
+                            myEditIcon(Modifier.align(Alignment.Center))
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    LookaheadScope {
+        Box(
+            modifier.clickable {
+                mode =
+                    if (mode == FabToolbarMode.Fab) {
+                        FabToolbarMode.Toolbar
+                    } else {
+                        FabToolbarMode.Fab
+                    }
+            }
+        ) {
+            Box(
+                Modifier.align(Alignment.Center),
+            ) {
+                // Slot 0 - Toolbar position
+                if (mode == FabToolbarMode.Toolbar) {
+                    // The Toolbar container should also place the Edit Icon at this state
+                    myToolbar(Modifier.align(Alignment.Center))
+                }
+            }
+            Box(Modifier.size(80.dp).align(Alignment.CenterEnd)) {
+                // Slot 1 - Fab position
+                if (mode == FabToolbarMode.Fab) {
+                    // We pull out the Edit Icon in this state
+                    myToolbar(Modifier.align(Alignment.Center))
+                    myEditIcon(Modifier.align(Alignment.Center))
+                }
+            }
+        }
+    }
+}
+
+enum class FabToolbarMode {
+    Fab,
+    Toolbar
+}
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadInScrollingColumn.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadInScrollingColumn.kt
new file mode 100644
index 0000000..69173ea
--- /dev/null
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadInScrollingColumn.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2024 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.animation.demos.lookahead
+
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
+import androidx.compose.animation.core.VisibilityThreshold
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.demos.layoutanimation.turquoiseColors
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+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.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentWithReceiverOf
+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.geometry.Rect
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.LookaheadScope
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
+
+/**
+ * A simple example showing how [animateBounds] behaves when animating from/to a scrolling layout.
+ *
+ * Note that despite the items position changing due to the scroll, it does not affect or trigger an
+ * animation.
+ */
+@OptIn(ExperimentalSharedTransitionApi::class)
+@Composable
+@Preview
+fun LookaheadInScrollingColumn() {
+    var displayInScroller by remember { mutableStateOf(false) }
+    val movableContent = remember {
+        movableContentWithReceiverOf<LookaheadScope> {
+            Box(
+                Modifier.zIndex(1f)
+                    .let {
+                        if (displayInScroller) {
+                            it.height(80.dp).fillMaxWidth()
+                        } else {
+                            it.size(150.dp)
+                        }
+                    }
+                    .animateBounds(
+                        lookaheadScope = this@movableContentWithReceiverOf,
+                        boundsTransform = { _, _ ->
+                            spring(stiffness = 50f, visibilityThreshold = Rect.VisibilityThreshold)
+                        }
+                    )
+                    .clickable { displayInScroller = !displayInScroller }
+                    .background(color, RoundedCornerShape(10.dp))
+            )
+        }
+    }
+
+    Box(Modifier.fillMaxSize()) {
+        LookaheadScope {
+            Column(
+                modifier =
+                    Modifier.fillMaxSize().verticalScroll(rememberScrollState(0)).padding(10.dp),
+                verticalArrangement = Arrangement.spacedBy(10.dp)
+            ) {
+                Text("Click Yellow box to animate to/from scrolling list.")
+                repeat(6) {
+                    Box(
+                        Modifier.fillMaxWidth()
+                            .background(turquoiseColors[it % 6], RoundedCornerShape(10.dp))
+                            .height(80.dp)
+                    )
+                }
+                if (displayInScroller) {
+                    movableContent()
+                }
+                repeat(6) {
+                    Box(
+                        Modifier.animateBounds(lookaheadScope = this@LookaheadScope)
+                            .background(turquoiseColors[it % 6], RoundedCornerShape(10.dp))
+                            .height(80.dp)
+                            .fillMaxWidth()
+                    )
+                }
+            }
+            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.BottomEnd) {
+                if (!displayInScroller) {
+                    movableContent()
+                }
+            }
+        }
+    }
+}
+
+private val color = Color(0xffffcc5c)
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadSamplesDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadSamplesDemo.kt
index f0375d7..8af930f 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadSamplesDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadSamplesDemo.kt
@@ -16,17 +16,56 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
+import androidx.compose.animation.core.ExperimentalAnimatableApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
 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.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.LookaheadScope
 import androidx.compose.ui.samples.LookaheadLayoutCoordinatesSample
-import androidx.compose.ui.samples.approachLayoutSample
 import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
 
 @Preview
 @Composable
 fun LookaheadSamplesDemo() {
     Column {
-        approachLayoutSample()
+        ApproachLayoutSample0()
         LookaheadLayoutCoordinatesSample()
     }
 }
+
+@OptIn(ExperimentalAnimatableApi::class, ExperimentalSharedTransitionApi::class)
+@Composable
+public fun ApproachLayoutSample0() {
+    var fullWidth by remember { mutableStateOf(false) }
+    LookaheadScope {
+        Row(
+            (if (fullWidth) Modifier.fillMaxWidth() else Modifier.width(100.dp))
+                .height(200.dp)
+                // Use the custom modifier created above to animate the constraints passed
+                // to the child, and therefore resize children in an animation.
+                .animateBounds(this@LookaheadScope)
+                .clickable { fullWidth = !fullWidth }
+        ) {
+            Box(
+                Modifier.weight(1f).fillMaxHeight().background(Color(0xffff6f69)),
+            )
+            Box(Modifier.weight(2f).fillMaxHeight().background(Color(0xffffcc5c)))
+        }
+    }
+}
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithAnimatedContentSize.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithAnimatedContentSize.kt
index 36f8fec..34c468d 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithAnimatedContentSize.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithAnimatedContentSize.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.animation.animateContentSize
 import androidx.compose.animation.demos.gesture.pastelColors
 import androidx.compose.foundation.background
@@ -34,6 +36,7 @@
 import androidx.compose.ui.zIndex
 import kotlinx.coroutines.delay
 
+@OptIn(ExperimentalSharedTransitionApi::class)
 @Preview
 @Composable
 fun LookaheadWithAnimatedContentSize() {
@@ -56,7 +59,12 @@
                     Box(Modifier.fillMaxWidth().height(200.dp).background(Color.White))
                 }
             }
-            Box(Modifier.animateBounds().fillMaxWidth().height(100.dp).background(pastelColors[1]))
+            Box(
+                Modifier.animateBounds(this@LookaheadScope)
+                    .fillMaxWidth()
+                    .height(100.dp)
+                    .background(pastelColors[1])
+            )
         }
     }
 }
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt
index 24f929f..48ffde5 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.animation.demos.gesture.pastelColors
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
@@ -46,6 +48,7 @@
 import androidx.compose.ui.layout.LookaheadScope
 import androidx.compose.ui.unit.dp
 
+@OptIn(ExperimentalSharedTransitionApi::class)
 @Suppress("UnusedBoxWithConstraintsScope")
 @Composable
 fun LookaheadWithBoxWithConstraints() {
@@ -59,6 +62,7 @@
                 Column(
                     Modifier.fillMaxHeight()
                         .animateBounds(
+                            this@LookaheadScope,
                             if (halfSize) Modifier.fillMaxSize(0.5f) else Modifier.fillMaxWidth()
                         )
                         .background(pastelColors[2]),
@@ -96,7 +100,10 @@
                         BoxWithConstraints {
                             Column(
                                 if (animate) {
-                                        Modifier.animateBounds(Modifier.fillMaxWidth())
+                                        Modifier.animateBounds(
+                                            lookaheadScope = this@LookaheadScope,
+                                            Modifier.fillMaxWidth()
+                                        )
                                     } else {
                                         Modifier.fillMaxWidth()
                                     }
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithFlowRowDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithFlowRowDemo.kt
index 6e8296d..d3dd741 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithFlowRowDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithFlowRowDemo.kt
@@ -14,8 +14,12 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalSharedTransitionApi::class)
+
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.background
@@ -76,17 +80,26 @@
                 ) {
                     Box(
                         Modifier.height(50.dp)
-                            .animateBounds(Modifier.fillMaxWidth(if (isHorizontal) 0.4f else 1f))
+                            .animateBounds(
+                                lookaheadScope = this@LookaheadScope,
+                                Modifier.fillMaxWidth(if (isHorizontal) 0.4f else 1f)
+                            )
                             .background(colors[0], RoundedCornerShape(10))
                     )
                     Box(
                         Modifier.height(50.dp)
-                            .animateBounds(Modifier.fillMaxWidth(if (isHorizontal) 0.2f else 0.4f))
+                            .animateBounds(
+                                lookaheadScope = this@LookaheadScope,
+                                Modifier.fillMaxWidth(if (isHorizontal) 0.2f else 0.4f)
+                            )
                             .background(colors[1], RoundedCornerShape(10))
                     )
                     Box(
                         Modifier.height(50.dp)
-                            .animateBounds(Modifier.fillMaxWidth(if (isHorizontal) 0.2f else 0.4f))
+                            .animateBounds(
+                                lookaheadScope = this@LookaheadScope,
+                                Modifier.fillMaxWidth(if (isHorizontal) 0.2f else 0.4f)
+                            )
                             .background(colors[2], RoundedCornerShape(10))
                     )
                 }
@@ -136,12 +149,19 @@
             var expanded by remember { mutableStateOf(false) }
             Box(
                 modifier =
-                    Modifier.animateBounds(Modifier.widthIn(max = 600.dp)).background(Color.Red)
+                    Modifier.animateBounds(
+                            lookaheadScope = this@LookaheadScope,
+                            Modifier.widthIn(max = 600.dp)
+                        )
+                        .background(Color.Red)
             ) {
                 val height = animateDpAsState(targetValue = if (expanded) 500.dp else 300.dp)
                 Box(
                     modifier =
-                        Modifier.animateBounds(Modifier.fillMaxWidth().height(height.value))
+                        Modifier.animateBounds(
+                                lookaheadScope = this@LookaheadScope,
+                                Modifier.fillMaxWidth().height(height.value)
+                            )
                             .clickable { expanded = !expanded }
                 )
             }
@@ -155,8 +175,8 @@
                     modifier =
                         Modifier.size(200.dp)
                             .animateBounds(
+                                lookaheadScope = this@LookaheadScope,
                                 Modifier.wrapContentWidth().heightIn(min = 156.dp),
-                                debug = true
                             )
                             .background(Color.Blue)
                 ) {
@@ -166,8 +186,8 @@
                     modifier =
                         Modifier.size(200.dp)
                             .animateBounds(
+                                lookaheadScope = this@LookaheadScope,
                                 Modifier.wrapContentWidth().heightIn(min = 156.dp),
-                                debug = true
                             )
                             .background(Color.Yellow)
                 ) {
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithIntrinsicsDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithIntrinsicsDemo.kt
index c359741..a38e88b 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithIntrinsicsDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithIntrinsicsDemo.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -43,6 +45,7 @@
 import androidx.compose.ui.layout.LookaheadScope
 import androidx.compose.ui.unit.dp
 
+@OptIn(ExperimentalSharedTransitionApi::class)
 @Composable
 fun LookaheadWithIntrinsicsDemo() {
     Column {
@@ -64,6 +67,7 @@
                 ) {
                     Box(
                         Modifier.animateBounds(
+                                lookaheadScope = this@LookaheadScope,
                                 if (isWide) Modifier.width(300.dp) else Modifier.width(150.dp)
                             )
                             .height(50.dp)
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithLazyColumn.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithLazyColumn.kt
index 6f6dc17b..0c0acba 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithLazyColumn.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithLazyColumn.kt
@@ -17,8 +17,11 @@
 package androidx.compose.animation.demos.lookahead
 
 import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.animation.core.MutableTransitionState
 import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.VisibilityThreshold
 import androidx.compose.animation.core.spring
 import androidx.compose.animation.demos.R
 import androidx.compose.animation.demos.gesture.pastelColors
@@ -47,6 +50,7 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.layout.LookaheadScope
@@ -54,6 +58,7 @@
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 
+@OptIn(ExperimentalSharedTransitionApi::class)
 @Preview
 @Composable
 fun LookaheadWithLazyColumn() {
@@ -74,10 +79,7 @@
                         LookaheadScope {
                             val title = remember {
                                 movableContentOf {
-                                    Text(
-                                        names[index],
-                                        Modifier.padding(20.dp).animateBounds(Modifier)
-                                    )
+                                    Text(names[index], Modifier.padding(20.dp).animateBounds(this))
                                 }
                             }
                             val image = remember {
@@ -89,9 +91,16 @@
                                             modifier =
                                                 Modifier.padding(10.dp)
                                                     .animateBounds(
+                                                        this,
                                                         if (expanded) Modifier.fillMaxWidth()
                                                         else Modifier.size(80.dp),
-                                                        spring(stiffness = Spring.StiffnessLow)
+                                                        { _, _ ->
+                                                            spring(
+                                                                Spring.DampingRatioNoBouncy,
+                                                                Spring.StiffnessLow,
+                                                                Rect.VisibilityThreshold
+                                                            )
+                                                        }
                                                     )
                                                     .clip(RoundedCornerShape(5.dp)),
                                             contentScale =
@@ -108,10 +117,17 @@
                                             modifier =
                                                 Modifier.padding(10.dp)
                                                     .animateBounds(
+                                                        lookaheadScope = this,
                                                         if (expanded)
                                                             Modifier.fillMaxWidth().aspectRatio(1f)
                                                         else Modifier.size(80.dp),
-                                                        spring(stiffness = Spring.StiffnessLow)
+                                                        { _, _ ->
+                                                            spring(
+                                                                Spring.DampingRatioNoBouncy,
+                                                                Spring.StiffnessLow,
+                                                                Rect.VisibilityThreshold
+                                                            )
+                                                        }
                                                     )
                                                     .background(
                                                         Color.LightGray,
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithMovableContentDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithMovableContentDemo.kt
index 2e82042..a02fc9a 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithMovableContentDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithMovableContentDemo.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.animation.core.DeferredTargetAnimation
 import androidx.compose.animation.core.ExperimentalAnimatableApi
 import androidx.compose.animation.core.VectorConverter
@@ -58,6 +60,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.round
 
+@OptIn(ExperimentalSharedTransitionApi::class)
 @Preview
 @Composable
 fun LookaheadWithMovableContentDemo() {
@@ -91,7 +94,7 @@
                         Modifier.padding(15.dp)
                             .height(80.dp)
                             .fillMaxWidth(weight)
-                            .animateBoundsInScope()
+                            .animateBounds(lookaheadScope = this@movableContentWithReceiverOf)
                             .background(color, RoundedCornerShape(20)),
                         contentAlignment = Alignment.Center
                     ) {
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithPopularBoxWithConstraintsUsage.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithPopularBoxWithConstraintsUsage.kt
index 7181a1b..f38a073 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithPopularBoxWithConstraintsUsage.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithPopularBoxWithConstraintsUsage.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.demos.R
 import androidx.compose.animation.demos.gesture.pastelColors
@@ -50,6 +52,7 @@
 import androidx.compose.ui.unit.dp
 import kotlinx.coroutines.delay
 
+@OptIn(ExperimentalSharedTransitionApi::class)
 @Preview
 @Composable
 fun LookaheadWithPopularBoxWithConstraintsUsage() {
@@ -67,7 +70,7 @@
     LookaheadScope {
         Box(
             Modifier.fillMaxSize()
-                .animateBounds(Modifier.padding(padding))
+                .animateBounds(this, Modifier.padding(padding))
                 .background(pastelColors[3])
         ) {
             DetailsContent()
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithScaffold.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithScaffold.kt
index c2ee5bd..08f1c8a 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithScaffold.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithScaffold.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.foundation.background
@@ -77,6 +79,7 @@
 private val colors =
     listOf(Color(0xffff6f69), Color(0xffffcc5c), Color(0xff264653), Color(0xff2a9d84))
 
+@OptIn(ExperimentalSharedTransitionApi::class)
 @Preview
 @Composable
 fun LookaheadWithScaffold() {
@@ -91,7 +94,10 @@
         Box(
             Modifier.fillMaxHeight()
                 .background(Color.Gray)
-                .animateBounds(if (hasPadding) Modifier.padding(bottom = 300.dp) else Modifier)
+                .animateBounds(
+                    this@LookaheadScope,
+                    if (hasPadding) Modifier.padding(bottom = 300.dp) else Modifier
+                )
         ) {
             var state by remember { mutableIntStateOf(0) }
             val titles =
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithSubcompose.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithSubcompose.kt
index 098f6a2..2ea3bd2 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithSubcompose.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithSubcompose.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -107,10 +109,11 @@
 }
 
 context(LookaheadScope)
+@OptIn(ExperimentalSharedTransitionApi::class)
 private fun Modifier.conditionallyAnimateBounds(
     shouldAnimate: Boolean,
     modifier: Modifier = Modifier
-) = if (shouldAnimate) this.animateBounds(modifier) else this.then(modifier)
+) = if (shouldAnimate) this.animateBounds(this@LookaheadScope, modifier) else this.then(modifier)
 
 private val colors =
     listOf(Color(0xffff6f69), Color(0xffffcc5c), Color(0xff2a9d84), Color(0xff264653))
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithTabRowDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithTabRowDemo.kt
index 07df45b..a83f31c 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithTabRowDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithTabRowDemo.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
@@ -50,6 +52,7 @@
 import androidx.compose.ui.unit.dp
 import kotlinx.coroutines.delay
 
+@OptIn(ExperimentalSharedTransitionApi::class)
 @Preview
 @Composable
 fun LookaheadWithTabRowDemo() {
@@ -63,7 +66,10 @@
             }
         Column(
             Modifier.fillMaxWidth()
-                .animateBounds(if (isWide) Modifier else Modifier.padding(end = 100.dp))
+                .animateBounds(
+                    this@LookaheadScope,
+                    if (isWide) Modifier else Modifier.padding(end = 100.dp)
+                )
                 .fillMaxHeight()
                 .background(Color(0xFFfffbd0))
         ) {
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/SceneHostExperiment.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/SceneHostExperiment.kt
index 1d877f5..1b94fc1 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/SceneHostExperiment.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/SceneHostExperiment.kt
@@ -19,6 +19,7 @@
 import androidx.compose.animation.core.AnimationVector2D
 import androidx.compose.animation.core.DeferredTargetAnimation
 import androidx.compose.animation.core.ExperimentalAnimatableApi
+import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.VectorConverter
 import androidx.compose.animation.core.spring
@@ -36,6 +37,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.layout.LookaheadScope
+import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.approachLayout
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntOffset
@@ -43,6 +45,7 @@
 import androidx.compose.ui.unit.round
 import androidx.compose.ui.unit.toOffset
 import androidx.compose.ui.unit.toSize
+import kotlinx.coroutines.CoroutineScope
 
 @Composable
 fun SceneHost(modifier: Modifier = Modifier, content: @Composable SceneScope.() -> Unit) {
@@ -149,3 +152,26 @@
             }
         }
 }
+
+context(LookaheadScope, Placeable.PlacementScope, CoroutineScope)
+@OptIn(ExperimentalAnimatableApi::class)
+internal fun DeferredTargetAnimation<IntOffset, AnimationVector2D>.updateTargetBasedOnCoordinates(
+    animationSpec: FiniteAnimationSpec<IntOffset>,
+): IntOffset {
+    coordinates?.let { coordinates ->
+        with(this@PlacementScope) {
+            val targetOffset = lookaheadScopeCoordinates.localLookaheadPositionOf(coordinates)
+            val animOffset =
+                updateTarget(
+                    targetOffset.round(),
+                    this@CoroutineScope,
+                    animationSpec,
+                )
+            val current =
+                lookaheadScopeCoordinates.localPositionOf(coordinates, Offset.Zero).round()
+            return (animOffset - current)
+        }
+    }
+
+    return IntOffset.Zero
+}
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/ScreenSizeChangeDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/ScreenSizeChangeDemo.kt
index 2717e49..497f9b2 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/ScreenSizeChangeDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/ScreenSizeChangeDemo.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -137,11 +139,13 @@
     }
 }
 
+@OptIn(ExperimentalSharedTransitionApi::class)
 @Composable
 fun Root(state: DisplayState) {
     SceneHost {
         Row(
             Modifier.animateBounds(
+                    this,
                     if (state == DisplayState.Compact) {
                         Modifier.wrapContentSize(align = Alignment.TopStart, unbounded = true)
                             .requiredWidth(800.dp)
@@ -322,10 +326,12 @@
     }
 }
 
+@OptIn(ExperimentalSharedTransitionApi::class)
 @Composable
 fun SceneScope.NavRail(state: DisplayState) {
     Column(
         Modifier.animateBounds(
+                this,
                 if (state == DisplayState.Tablet) Modifier.width(200.dp)
                 else Modifier.width(IntrinsicSize.Min)
             )
diff --git a/compose/animation/animation/samples/build.gradle b/compose/animation/animation/samples/build.gradle
index a3be858..66c33e58 100644
--- a/compose/animation/animation/samples/build.gradle
+++ b/compose/animation/animation/samples/build.gradle
@@ -36,11 +36,11 @@
     compileOnly(project(":annotation:annotation-sampled"))
 
     implementation(project(":compose:animation:animation"))
-    implementation("androidx.compose.foundation:foundation:1.2.1")
-    implementation("androidx.compose.material:material:1.2.1")
-    implementation("androidx.compose.material:material-icons-core:1.6.7")
-    implementation("androidx.compose.runtime:runtime:1.2.1")
-    implementation("androidx.compose.ui:ui-text:1.2.1")
+    implementation("androidx.compose.foundation:foundation:1.6.8")
+    implementation("androidx.compose.material:material:1.6.8")
+    implementation("androidx.compose.material:material-icons-core:1.6.8")
+    implementation("androidx.compose.runtime:runtime:1.6.8")
+    implementation("androidx.compose.ui:ui-text:1.6.8")
 }
 
 androidx {
diff --git a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimateBoundsModifierSample.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimateBoundsModifierSample.kt
new file mode 100644
index 0000000..70dbcfa
--- /dev/null
+++ b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimateBoundsModifierSample.kt
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2024 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.animation.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.animation.BoundsTransform
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.animateBounds
+import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.VisibilityThreshold
+import androidx.compose.animation.core.keyframesWithSpline
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.FlowRow
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentWithReceiverOf
+import androidx.compose.runtime.mutableIntStateOf
+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.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.LookaheadScope
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.round
+import androidx.compose.ui.util.fastForEach
+
+@OptIn(ExperimentalSharedTransitionApi::class)
+@Sampled
+@Composable
+private fun AnimateBounds_animateOnContentChange() {
+    // Example where the change in content triggers the layout change on the item with animateBounds
+    val textShort = remember { "Foo ".repeat(10) }
+    val textLong = remember { "Bar ".repeat(50) }
+
+    var toggle by remember { mutableStateOf(true) }
+
+    LookaheadScope {
+        Box(
+            modifier = Modifier.fillMaxSize().clickable { toggle = !toggle },
+            contentAlignment = Alignment.Center
+        ) {
+            Text(
+                text = if (toggle) textShort else textLong,
+                modifier =
+                    Modifier.fillMaxWidth(0.7f)
+                        .background(Color.LightGray)
+                        .animateBounds(this@LookaheadScope)
+                        .padding(10.dp),
+            )
+        }
+    }
+}
+
+@OptIn(ExperimentalSharedTransitionApi::class)
+@Sampled
+@Composable
+private fun AnimateBounds_withLayoutModifier() {
+    // Example showing the difference between providing a Layout Modifier as a parameter of
+    // `animateBounds` and chaining the Layout Modifier.
+
+    // We use `padding` in this example, as it provides an immediate change in layout to its child,
+    // but not the parent, which sees the same resulting layout. The difference can be seen in the
+    // Text (content under padding) and an accompanying Cyan Box (a sibling, under the same Row
+    // parent).
+    LookaheadScope {
+        val boundsTransform = remember {
+            BoundsTransform { _, _ ->
+                spring(stiffness = 50f, visibilityThreshold = Rect.VisibilityThreshold)
+            }
+        }
+
+        var toggleAnimation by remember { mutableStateOf(true) }
+
+        Column(Modifier.clickable { toggleAnimation = !toggleAnimation }) {
+            Text(
+                "See the difference in animation when the Layout Modifier is a parameter of animateBounds. Padding, in this example."
+            )
+            Spacer(Modifier.height(12.dp))
+            Text("Layout Modifier as a parameter.")
+            Row(Modifier.fillMaxWidth()) {
+                Box(
+                    Modifier.animateBounds(
+                            lookaheadScope = this@LookaheadScope,
+                            modifier =
+                                // By providing this Modifier as a parameter of `animateBounds`,
+                                // both content and parent see a gradual/animated change in Layout.
+                                Modifier.padding(
+                                    horizontal = if (toggleAnimation) 10.dp else 50.dp
+                                ),
+                            boundsTransform = boundsTransform
+                        )
+                        .background(Color.Red, RoundedCornerShape(12.dp))
+                        .height(50.dp)
+                ) {
+                    Text("Layout Content", Modifier.align(Alignment.Center))
+                }
+                Box(Modifier.size(50.dp).background(Color.Cyan, RoundedCornerShape(12.dp)))
+            }
+            Spacer(Modifier.height(12.dp))
+            Text("Layout Modifier after AnimateBounds.")
+            Row(Modifier.fillMaxWidth()) {
+                Box(
+                    Modifier.animateBounds(
+                            lookaheadScope = this@LookaheadScope,
+                            boundsTransform = boundsTransform
+                        )
+                        // The content is able to animate the change in padding, but since the
+                        // parent Layout sees no difference, the change in position is immediate.
+                        .padding(horizontal = if (toggleAnimation) 10.dp else 50.dp)
+                        .background(Color.Red, RoundedCornerShape(12.dp))
+                        .height(50.dp)
+                ) {
+                    Text("Layout Content", Modifier.align(Alignment.Center))
+                }
+                Box(Modifier.size(50.dp).background(Color.Cyan, RoundedCornerShape(12.dp)))
+            }
+            Spacer(Modifier.height(12.dp))
+            Text("Layout Modifier before AnimateBounds.")
+            Row(Modifier.fillMaxWidth()) {
+                Box(
+                    Modifier
+                        // The parent is able to see the change in position and the animated size,
+                        // so it can smoothly place both its children, but the content of the Box
+                        // cannot see the gradual changes so it remains constant.
+                        .padding(horizontal = if (toggleAnimation) 10.dp else 50.dp)
+                        .animateBounds(
+                            lookaheadScope = this@LookaheadScope,
+                            boundsTransform = boundsTransform
+                        )
+                        .background(Color.Red, RoundedCornerShape(12.dp))
+                        .height(50.dp)
+                ) {
+                    Text("Layout Content", Modifier.align(Alignment.Center))
+                }
+                Box(Modifier.size(50.dp).background(Color.Cyan, RoundedCornerShape(12.dp)))
+            }
+        }
+    }
+}
+
+@OptIn(
+    ExperimentalLayoutApi::class,
+    ExperimentalSharedTransitionApi::class,
+)
+@Sampled
+@Composable
+private fun AnimateBounds_inFlowRowSample() {
+    var itemRowCount by remember { mutableIntStateOf(1) }
+    val colors = remember { listOf(Color.Cyan, Color.Magenta, Color.Yellow, Color.Green) }
+
+    // A case showing `animateBounds` being used to animate layout changes driven by a parent Layout
+    LookaheadScope {
+        Column(Modifier.clickable { itemRowCount = if (itemRowCount != 2) 2 else 1 }) {
+            Text("Click to toggle animation.")
+            FlowRow(
+                modifier =
+                    Modifier.fillMaxWidth()
+                        // Note that the wrap content size changes for FlowRow as the content
+                        // adjusts
+                        // to one or two lines, we can simply use `animateContentSize()` to make
+                        // sure
+                        // all items are visible during their animation.
+                        .animateContentSize(),
+                // Try changing the arrangement as well!
+                horizontalArrangement = Arrangement.spacedBy(8.dp),
+                verticalArrangement = Arrangement.spacedBy(8.dp),
+                // We use the maxItems parameter to change the layout of the FlowRow at different
+                // states
+                maxItemsInEachRow = itemRowCount
+            ) {
+                colors.fastForEach {
+                    Box(
+                        Modifier.animateBounds(this@LookaheadScope)
+                            // Note the modifier order, we declare the background after
+                            // `animateBounds` to make sure it animates with the rest of the content
+                            .background(it, RoundedCornerShape(12.dp))
+                            .weight(weight = 1f, fill = true)
+                            .height(100.dp)
+                    )
+                }
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalSharedTransitionApi::class)
+@Sampled
+@Composable
+private fun AnimateBounds_usingKeyframes() {
+    var toggle by remember { mutableStateOf(true) }
+
+    // Example using BoundsTransform to calculate an animation using keyframes with splines.
+    LookaheadScope {
+        Box(Modifier.fillMaxSize().clickable { toggle = !toggle }) {
+            Text(
+                text = "Hello, World!",
+                textAlign = TextAlign.Center,
+                modifier =
+                    Modifier.align(if (toggle) Alignment.TopStart else Alignment.TopEnd)
+                        .animateBounds(
+                            lookaheadScope = this@LookaheadScope,
+                            boundsTransform = { initialBounds, targetBounds ->
+                                // We'll use a keyframe to emphasize the animation in position and
+                                // size.
+                                keyframesWithSpline {
+                                    durationMillis = 1200
+
+                                    // Emphasize with an increase in size
+                                    val size = targetBounds.size.times(2f)
+
+                                    // Emphasize the path with a slight curve at the halfway point
+                                    val position =
+                                        targetBounds.topLeft
+                                            .plus(initialBounds.topLeft)
+                                            .times(0.5f)
+                                            .plus(
+                                                Offset(
+                                                    // Consider the increase in size (from the
+                                                    // center,
+                                                    // to keep the Layout aligned at the keyframe)
+                                                    x = -(size.width - targetBounds.width) * 0.5f,
+                                                    // Emphasize the path with a vertical offset
+                                                    y = size.height * 0.5f
+                                                )
+                                            )
+
+                                    // Only need to define the intermediate keyframe, initial and
+                                    // target are implicit.
+                                    Rect(position, size).atFraction(0.5f).using(LinearEasing)
+                                }
+                            }
+                        )
+                        .background(Color.LightGray, RoundedCornerShape(50))
+                        .padding(10.dp)
+                        // Text is laid out with the animated fixed Constraints, relax constraints
+                        // back to wrap content to be able to center Align vertically.
+                        .wrapContentSize(Alignment.Center)
+            )
+        }
+    }
+}
+
+@OptIn(ExperimentalSharedTransitionApi::class)
+@Sampled
+@Composable
+private fun AnimateBounds_withMovableContent() {
+    // Example showing how to animate a Layout that can be presented on different Layout Composables
+    // as the state changes using `movableContent`.
+    var position by remember { mutableIntStateOf(-1) }
+
+    val movableContent = remember {
+        // To animate a Layout that can be presented in different Composables, we can use
+        // `animateBounds` with `movableContent`.
+        movableContentWithReceiverOf<LookaheadScope> {
+            Box(
+                Modifier.animateBounds(
+                        lookaheadScope = this@movableContentWithReceiverOf,
+                        boundsTransform = { _, _ ->
+                            spring(
+                                dampingRatio = Spring.DampingRatioLowBouncy,
+                                stiffness = Spring.StiffnessVeryLow,
+                                visibilityThreshold = Rect.VisibilityThreshold
+                            )
+                        }
+                    )
+                    // Our movableContent can always fill its container in this example.
+                    .fillMaxSize()
+                    .background(Color.Cyan, RoundedCornerShape(8.dp))
+            )
+        }
+    }
+
+    LookaheadScope {
+        Box(Modifier.fillMaxSize()) {
+            // Initial container of our Layout, at the center of the screen.
+            Box(
+                Modifier.size(200.dp)
+                    .border(3.dp, Color.Red, RoundedCornerShape(8.dp))
+                    .align(Alignment.Center)
+                    .clickable { position = -1 }
+            ) {
+                if (position < 0) {
+                    movableContent()
+                }
+            }
+
+            repeat(4) { index ->
+                // Four additional Boxes where our content may be move to.
+                Box(
+                    Modifier.size(100.dp)
+                        .border(2.dp, Color.Blue, RoundedCornerShape(8.dp))
+                        .align { size, space, _ ->
+                            val horizontal = if (index % 2 == 0) 0.15f else 0.85f
+                            val vertical = if (index < 2) 0.15f else 0.85f
+
+                            Offset(
+                                    x = (space.width - size.width) * horizontal,
+                                    y = (space.height - size.height) * vertical
+                                )
+                                .round()
+                        }
+                        .clickable { position = index }
+                ) {
+                    if (position == index) {
+                        // The call to movable content will trigger `Modifier.animateBounds()` to
+                        // animate the content's position and size from its previous state.
+                        movableContent()
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/AnimateBoundsTest.kt b/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/AnimateBoundsTest.kt
new file mode 100644
index 0000000..3987062
--- /dev/null
+++ b/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/AnimateBoundsTest.kt
@@ -0,0 +1,561 @@
+/*
+ * Copyright 2024 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.animation
+
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.keyframes
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+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.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentWithReceiverOf
+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.draw.drawBehind
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.LookaheadScope
+import androidx.compose.ui.layout.boundsInParent
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.positionInParent
+import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.lerp
+import androidx.compose.ui.unit.round
+import androidx.compose.ui.util.fastRoundToInt
+import androidx.compose.ui.util.lerp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import kotlin.math.roundToInt
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalSharedTransitionApi::class)
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+class AnimateBoundsTest {
+
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun animatePosition() =
+        with(rule.density) {
+            val frames = 14 // Even number to reliably test at half duration
+            val durationMillis = frames * 16
+            val rootSizePx = 100
+            val boxSizePX = 20
+
+            var boxPosition = IntOffset.Zero
+
+            var isAtStart by mutableStateOf(true)
+
+            rule.setContent {
+                Box(modifier = Modifier.size(rootSizePx.toDp())) {
+                    LookaheadScope {
+                        Box(
+                            modifier =
+                                Modifier.align(
+                                        if (isAtStart) Alignment.TopStart else Alignment.BottomEnd
+                                    )
+                                    .size(boxSizePX.toDp())
+                                    .animateBounds(
+                                        lookaheadScope = this@LookaheadScope,
+                                        boundsTransform = { _, _ ->
+                                            tween(durationMillis, easing = LinearEasing)
+                                        }
+                                    )
+                                    .drawBehind { drawRect(Color.LightGray) }
+                                    .onGloballyPositioned {
+                                        boxPosition = it.positionInParent().round()
+                                    }
+                        )
+                    }
+                }
+            }
+            rule.waitForIdle()
+
+            // At TopStart (0, 0)
+            assertEquals(IntOffset.Zero, boxPosition)
+
+            // AutoAdvance off to test animation at different points
+            rule.mainClock.autoAdvance = false
+            isAtStart = false
+            rule.waitForIdle()
+            rule.mainClock.advanceTimeByFrame()
+            rule.mainClock.advanceTimeByFrame()
+
+            // Advance to the middle of the animation
+            rule.mainClock.advanceTimeBy(durationMillis / 2L)
+
+            val expectedPosPx = (rootSizePx - boxSizePX) * 0.5f
+            val expectedIntOffset = Offset(expectedPosPx, expectedPosPx).round()
+            assertEquals(expectedIntOffset, boxPosition)
+
+            // AutoAdvance ON to finish the animation
+            rule.mainClock.autoAdvance = true
+            rule.waitForIdle()
+
+            // At BottomEnd (parentSize - boxSize, parentSize - boxSize)
+            val expectedFinalPos = rootSizePx - boxSizePX
+            assertEquals(IntOffset(expectedFinalPos, expectedFinalPos), boxPosition)
+        }
+
+    @Test
+    fun animateSize() =
+        with(rule.density) {
+            val frames = 14 // Even number to reliable test at half duration
+            val durationMillis = frames * 16
+            val rootSizePx = 400
+            val boxSizeSmallPx = rootSizePx * 0.25f
+            val boxSizeLargePx = rootSizePx * 0.5f
+
+            val expectedLargeSize = Size(boxSizeLargePx, boxSizeLargePx)
+            val expectedSmallSize = Size(boxSizeSmallPx, boxSizeSmallPx)
+
+            var boxSize = IntSize.Zero
+
+            var isExpanded by mutableStateOf(false)
+
+            rule.setContent {
+                Box(Modifier.size(rootSizePx.toDp())) {
+                    LookaheadScope {
+                        Box(
+                            Modifier.size(
+                                    if (isExpanded) boxSizeLargePx.toDp() else boxSizeSmallPx.toDp()
+                                )
+                                .animateBounds(
+                                    lookaheadScope = this,
+                                    boundsTransform = { _, _ ->
+                                        tween(
+                                            durationMillis = durationMillis,
+                                            easing = LinearEasing
+                                        )
+                                    }
+                                )
+                                .drawBehind { drawRect(Color.LightGray) }
+                                .onGloballyPositioned { boxSize = it.size }
+                        )
+                    }
+                }
+            }
+            rule.waitForIdle()
+            assertEquals(expectedSmallSize.round(), boxSize)
+
+            // AutoAdvance off to test animation at different points
+            rule.mainClock.autoAdvance = false
+            isExpanded = true
+            rule.waitForIdle()
+            rule.mainClock.advanceTimeByFrame()
+            rule.mainClock.advanceTimeByFrame()
+
+            // 1-frame latency. TODO: Can we fix this?
+            rule.mainClock.advanceTimeByFrame()
+
+            // Advance to approx. the middle of the animation
+            rule.mainClock.advanceTimeBy(durationMillis / 2L)
+
+            val expectedMidIntSize = (expectedLargeSize + expectedSmallSize).times(0.5f).round()
+            assertEquals(expectedMidIntSize, boxSize)
+
+            // AutoAdvance ON to finish the animation
+            rule.mainClock.autoAdvance = true
+            rule.waitForIdle()
+
+            assertEquals(expectedLargeSize.round(), boxSize)
+        }
+
+    @Test
+    fun animateBounds() =
+        with(rule.density) {
+            val frames = 14 // Even number to reliable test at half duration
+            val durationMillis = frames * 16
+            val rootSizePx = 400
+            val boxSizeSmallPx = rootSizePx * 0.25f
+            val boxSizeLargePx = rootSizePx * 0.5f
+
+            val expectedLargeSize = Size(boxSizeLargePx, boxSizeLargePx)
+            val expectedSmallSize = Size(boxSizeSmallPx, boxSizeSmallPx)
+            val expectedFinalPos = rootSizePx - boxSizeLargePx
+
+            var boxBounds = Rect(Offset.Zero, Size.Zero)
+
+            var toggle by mutableStateOf(false)
+
+            rule.setContent {
+                Box(Modifier.size(rootSizePx.toDp())) {
+                    LookaheadScope {
+                        Box(
+                            Modifier.then(
+                                    if (toggle) {
+                                        Modifier.align(Alignment.BottomEnd)
+                                            .size(boxSizeLargePx.toDp())
+                                    } else {
+                                        Modifier.align(Alignment.TopStart)
+                                            .size(boxSizeSmallPx.toDp())
+                                    }
+                                )
+                                .animateBounds(
+                                    lookaheadScope = this,
+                                    boundsTransform = { _, _ ->
+                                        tween(
+                                            durationMillis = durationMillis,
+                                            easing = LinearEasing
+                                        )
+                                    }
+                                )
+                                .drawBehind { drawRect(Color.Yellow) }
+                                .onGloballyPositioned { boxBounds = it.boundsInParent() }
+                        )
+                    }
+                }
+            }
+            rule.waitForIdle()
+            assertEquals(Rect(Offset.Zero, expectedSmallSize), boxBounds)
+
+            // AutoAdvance off to test animation at different points
+            rule.mainClock.autoAdvance = false
+            toggle = true
+            rule.waitForIdle()
+            rule.mainClock.advanceTimeByFrame()
+            rule.mainClock.advanceTimeByFrame()
+
+            // Advance to the middle of the animation
+            rule.mainClock.advanceTimeBy(durationMillis / 2L)
+            rule.waitForIdle()
+
+            // Calculate expected bounds
+            val expectedMidSize = (expectedLargeSize + expectedSmallSize).times(0.5f)
+            val expectedMidPosition = (rootSizePx - boxSizeLargePx) * 0.5f
+            val expectedMidOffset = Offset(expectedMidPosition, expectedMidPosition)
+            val expectedMidBounds = Rect(expectedMidOffset, expectedMidSize)
+
+            assertEquals(expectedMidBounds, boxBounds)
+
+            // AutoAdvance ON to finish the animation
+            rule.mainClock.autoAdvance = true
+            rule.waitForIdle()
+
+            assertEquals(
+                Rect(Offset(expectedFinalPos, expectedFinalPos), expectedLargeSize),
+                boxBounds
+            )
+        }
+
+    @Test
+    fun animateBounds_withIntermediateModifier() =
+        with(rule.density) {
+            val durationMillis = 10 * 16
+
+            var toggleAnimation by mutableStateOf(true)
+
+            val rootWidthPx = 100
+            val padding1Px = 10
+            val padding2Px = 20
+
+            var positionA = IntOffset(-1, -1)
+            var positionB = IntOffset(-1, 1)
+
+            // Change the padding on state change to trigger the animation
+            fun Modifier.applyPadding(): Modifier =
+                this.padding(
+                    horizontal =
+                        if (toggleAnimation) {
+                            padding1Px.toDp()
+                        } else {
+                            padding2Px.toDp()
+                        }
+                )
+
+            rule.setContent {
+                // Based on sample `AnimateBounds_withLayoutModifier`
+                LookaheadScope {
+                    Column(Modifier.width(rootWidthPx.toDp())) {
+                        Row(Modifier.fillMaxWidth()) {
+                            Box(
+                                Modifier.animateBounds(
+                                    lookaheadScope = this@LookaheadScope,
+                                    modifier = Modifier.applyPadding(),
+                                    boundsTransform = { _, _,
+                                        ->
+                                        tween(durationMillis, easing = LinearEasing)
+                                    }
+                                )
+                            ) {
+                                Box(
+                                    Modifier.onGloballyPositioned {
+                                        positionA = it.positionInRoot().round()
+                                    }
+                                )
+                            }
+                        }
+                        Row(Modifier.fillMaxWidth()) {
+                            Box(
+                                Modifier.animateBounds(
+                                        lookaheadScope = this@LookaheadScope,
+                                        boundsTransform = { _, _,
+                                            ->
+                                            tween(durationMillis, easing = LinearEasing)
+                                        }
+                                    )
+                                    .applyPadding()
+                            ) {
+                                Box(
+                                    Modifier.onGloballyPositioned {
+                                        positionB = it.positionInRoot().round()
+                                    }
+                                )
+                            }
+                        }
+                    }
+                }
+            }
+            rule.waitForIdle()
+
+            assertEquals(positionA, IntOffset(padding1Px, 0))
+            assertEquals(positionB, IntOffset(padding1Px, 0))
+
+            rule.mainClock.autoAdvance = false
+            toggleAnimation = !toggleAnimation
+            rule.mainClock.advanceTimeByFrame()
+            rule.mainClock.advanceTimeByFrame()
+
+            // We measure at the first animated frame
+            rule.mainClock.advanceTimeByFrame()
+
+            // Box A has a continuous change in value from having the Modifier as a parameter
+            val expectedPosA =
+                lerp(padding1Px.toFloat(), padding2Px.toFloat(), 16f / durationMillis)
+            assertEquals(positionA, IntOffset(expectedPosA.fastRoundToInt(), 0))
+            // Box B has an immediate change in value from chaining the Modifier
+            assertEquals(positionB, IntOffset(padding2Px, 0))
+        }
+
+    @Test
+    fun animateBounds_usingMovableContent() =
+        with(rule.density) {
+            val frames = 14 // Even number to reliable test at half duration
+            val durationMillis = frames * 16
+
+            val itemASizePx = 30
+            val itemAOffset = IntOffset(70, 70)
+
+            val itemBSizePx = 50
+            val itemBOffset = IntOffset(110, 110)
+
+            var isBoxAtSlotA by mutableStateOf(true)
+
+            var boxPosition = IntOffset.Zero
+            var boxSize = IntSize.Zero
+
+            rule.setContent {
+                val movableBox = remember {
+                    movableContentWithReceiverOf<LookaheadScope> {
+                        Box(
+                            modifier =
+                                Modifier.fillMaxSize()
+                                    .animateBounds(
+                                        lookaheadScope = this,
+                                        boundsTransform = { _, _ ->
+                                            tween(
+                                                durationMillis = durationMillis,
+                                                easing = LinearEasing
+                                            )
+                                        }
+                                    )
+                                    .onGloballyPositioned {
+                                        boxPosition = it.positionInRoot().round()
+                                        boxSize = it.size
+                                    }
+                        )
+                    }
+                }
+
+                LookaheadScope {
+                    Box {
+                        Box(Modifier.offset { itemAOffset }.size(itemASizePx.toDp())) {
+                            // Slot A
+                            if (isBoxAtSlotA) {
+                                movableBox()
+                            }
+                        }
+                        Box(Modifier.offset { itemBOffset }.size(itemBSizePx.toDp())) {
+                            // Slot B
+                            if (!isBoxAtSlotA) {
+                                movableBox()
+                            }
+                        }
+                    }
+                }
+            }
+            rule.waitForIdle()
+
+            // Initial conditions
+            assertEquals(itemAOffset, boxPosition)
+            assertEquals(IntSize(itemASizePx, itemASizePx), boxSize)
+
+            // AutoAdvance off to test animation at different points
+            rule.mainClock.autoAdvance = false
+            isBoxAtSlotA = false
+            rule.waitForIdle()
+            rule.mainClock.advanceTimeByFrame()
+            rule.mainClock.advanceTimeByFrame()
+
+            // Advance to the middle of the animation
+            rule.mainClock.advanceTimeBy(durationMillis / 2L)
+            rule.waitForIdle()
+
+            // Evaluate with expected values at half the animation
+            val sizeAtHalfDuration = (itemASizePx + itemBSizePx) / 2
+            assertEquals((itemAOffset + itemBOffset).div(2f), boxPosition)
+            assertEquals(IntSize(sizeAtHalfDuration, sizeAtHalfDuration), boxSize)
+
+            // AutoAdvance ON to finish the animation
+            rule.mainClock.autoAdvance = true
+            rule.waitForIdle()
+
+            assertEquals(itemBOffset, boxPosition)
+            assertEquals(IntSize(itemBSizePx, itemBSizePx), boxSize)
+        }
+
+    @Ignore("b/362340158 - Around 10% flaky on local tests")
+    @Test
+    fun animateBounds_scrollBehavior() =
+        with(rule.density) {
+            val itemSizePx = 30f
+            val keyFrameOffset = itemSizePx * 5
+
+            var isAnimateScroll by mutableStateOf(false)
+            val scrollState = ScrollState(0)
+
+            var item0Position = IntOffset(-1, -1)
+
+            rule.setContent {
+                LookaheadScope {
+                    Column(Modifier.size(itemSizePx.toDp()).verticalScroll(scrollState)) {
+                        repeat(2) { index ->
+                            Box(
+                                modifier =
+                                    Modifier.size(itemSizePx.toDp())
+                                        .animateBounds(
+                                            lookaheadScope = this@LookaheadScope,
+                                            boundsTransform = { initial, _ ->
+                                                // Drive the start position to a specific value, by
+                                                // default
+                                                // the animation should not happen, and so we should
+                                                // never
+                                                // be able to read that value.
+                                                keyframes {
+                                                    Rect(Offset(0f, keyFrameOffset), initial.size)
+                                                        .at(0)
+                                                        .using(LinearEasing)
+                                                }
+                                            },
+                                            animateMotionFrameOfReference = isAnimateScroll
+                                        )
+                                        .onGloballyPositioned {
+                                            if (index == 0) {
+                                                item0Position = it.positionInRoot().round()
+                                            }
+                                        }
+                            )
+                        }
+                    }
+                }
+            }
+            // First test without animating scroll, note that we still handle the clock, as to allow
+            // any animation to play after we change the scroll.
+            rule.waitForIdle()
+            rule.mainClock.autoAdvance = false
+
+            runBlocking { scrollState.scrollBy(itemSizePx) }
+
+            // Let animations play for the first frame
+            rule.mainClock.advanceTimeByFrame()
+            rule.mainClock.advanceTimeByFrame()
+
+            // Expected position should immediately reflect scroll changes since we are not
+            // animating it
+            assertEquals(IntOffset(0, -itemSizePx.fastRoundToInt()), item0Position)
+
+            // Finish any pending animations
+            rule.mainClock.autoAdvance = true
+            rule.waitForIdle()
+
+            // Enable scroll animation
+            isAnimateScroll = true
+            rule.waitForIdle()
+            rule.mainClock.autoAdvance = false
+
+            // Not sure why, but we need to run this scroll within a runOnIdle to complete the test
+            // consistently across devices.
+            rule.runOnIdle {
+                runBlocking {
+                    // Scroll back into starting position
+                    scrollState.scrollBy(-itemSizePx)
+                }
+            }
+
+            rule.mainClock.advanceTimeByFrame()
+            rule.mainClock.advanceTimeByFrame()
+
+            // Position should correspond to the exaggerated keyframe offset.
+            // Note that the keyframe is actually defined around the item's center
+            assertEquals(
+                Offset(
+                        // Center position at x = 0
+                        x = 0f,
+                        // keyframeOffset - (previousScrollOffset) + itemCenterY
+                        y = keyFrameOffset
+                    )
+                    .round(),
+                item0Position
+            )
+        }
+
+    private fun Size.round(): IntSize = IntSize(width.roundToInt(), height.roundToInt())
+
+    private operator fun Size.plus(other: Size) = Size(width + other.width, height + other.height)
+
+    private operator fun Size.minus(other: Size) = Size(width - other.width, height - other.height)
+
+    private operator fun IntSize.minus(other: IntSize) =
+        IntSize(width - other.width, height - other.height)
+
+    private operator fun Rect.minus(other: Rect) =
+        Rect(offset = this.topLeft - other.topLeft, size = this.size - other.size)
+}
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimateBoundsModifier.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimateBoundsModifier.kt
new file mode 100644
index 0000000..388b244
--- /dev/null
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimateBoundsModifier.kt
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2024 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.animation
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector4D
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.animation.core.VisibilityThreshold
+import androidx.compose.animation.core.spring
+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.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.isSpecified
+import androidx.compose.ui.geometry.isUnspecified
+import androidx.compose.ui.layout.ApproachLayoutModifierNode
+import androidx.compose.ui.layout.ApproachMeasureScope
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.LookaheadScope
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.positionInParent
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.round
+import androidx.compose.ui.unit.roundToIntSize
+import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.util.fastRoundToInt
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.launch
+
+/**
+ * [Modifier] to animate layout changes (position and/or size) that occur within a [LookaheadScope].
+ *
+ * So, the given [lookaheadScope] defines the coordinate space considered to trigger an animation.
+ * For example, if [lookaheadScope] was defined at the root of the app hierarchy, then any layout
+ * changes visible within the screen will trigger an animation, if it, in contrast was defined
+ * within a scrolling parent, then, as long the [LookaheadScope] scrolls with is content, no
+ * animation will be triggered, as there will be no changes within its coordinate space.
+ *
+ * The animation is driven with a [FiniteAnimationSpec] produced by the given [BoundsTransform]
+ * function, which you may use to customize the animations based on the initial and target bounds.
+ *
+ * Do note that certain Layout Modifiers when chained with [animateBounds], may only cause an
+ * immediate observable change to either the child or the parent Layout which can result in
+ * undesired behavior. For those cases you can instead provide it to the [modifier] parameter. This
+ * allows [animateBounds] to envelop the size and constraints change and propagate them gradually to
+ * both its parent and child Layout.
+ *
+ * You may see the difference when supplying a Layout Modifier in [modifier] on the following
+ * example:
+ *
+ * @sample androidx.compose.animation.samples.AnimateBounds_withLayoutModifier
+ *
+ * By default, changes in position under [LayoutCoordinates.introducesMotionFrameOfReference] are
+ * excluded from the animation and are instead immediately applied, as they are expected to be
+ * frequent/continuous (to handle Layouts under Scroll). You may change this behavior by passing
+ * [animateMotionFrameOfReference] as `true`. Keep in mind, doing that under a scroll may result in
+ * the Layout "chasing" the scroll offset, as it will constantly animate to the latest position.
+ *
+ * A basic use-case is animating a layout based on content changes, such as the String changing on a
+ * Text:
+ *
+ * @sample androidx.compose.animation.samples.AnimateBounds_animateOnContentChange
+ *
+ * It also provides an easy way to animate layout changes of a complex Composable Layout:
+ *
+ * @sample androidx.compose.animation.samples.AnimateBounds_inFlowRowSample
+ *
+ * Since [BoundsTransform] is called when initiating an animation, you may also use it to calculate
+ * a keyframe based animation:
+ *
+ * @sample androidx.compose.animation.samples.AnimateBounds_usingKeyframes
+ *
+ * It may also be used together with [movableContent][androidx.compose.runtime.movableContentOf] as
+ * long as the given [LookaheadScope] is in a common place within the Layout hierarchy of the slots
+ * presenting the `movableContent`:
+ *
+ * @sample androidx.compose.animation.samples.AnimateBounds_withMovableContent
+ * @param lookaheadScope The scope from which this [animateBounds] will calculate its animations
+ *   from. This implies that as long as you're expecting an animation the reference of the given
+ *   [LookaheadScope] shouldn't change, otherwise you may get unexpected behavior.
+ * @param modifier Optional intermediate Modifier, may be used in cases where otherwise immediate
+ *   layout changes are perceived as gradual by both the parent and child Layout.
+ * @param boundsTransform Produce a customized [FiniteAnimationSpec] based on the initial and target
+ *   bounds, called when an animation is triggered.
+ * @param animateMotionFrameOfReference When `true`, changes under
+ *   [LayoutCoordinates.introducesMotionFrameOfReference] (for continuous positional changes, such
+ *   as Scroll Offset) are included when calculating an animation. `false` by default, where the
+ *   changes are instead applied directly into the layout without triggering an animation.
+ * @see ApproachLayoutModifierNode
+ * @see LookaheadScope
+ */
+@ExperimentalSharedTransitionApi // Depends on BoundsTransform
+public fun Modifier.animateBounds(
+    lookaheadScope: LookaheadScope,
+    modifier: Modifier = Modifier,
+    boundsTransform: BoundsTransform = DefaultBoundsTransform,
+    animateMotionFrameOfReference: Boolean = false,
+): Modifier =
+    this.then(
+            BoundsAnimationElement(
+                lookaheadScope = lookaheadScope,
+                boundsTransform = boundsTransform,
+                // Measure with original constraints.
+                // The layout of this element will still be the animated lookahead size.
+                resolveMeasureConstraints = { _, constraints -> constraints },
+                animateMotionFrameOfReference = animateMotionFrameOfReference,
+            )
+        )
+        .then(modifier)
+        .then(
+            BoundsAnimationElement(
+                lookaheadScope = lookaheadScope,
+                boundsTransform = boundsTransform,
+                resolveMeasureConstraints = { animatedSize, _ ->
+                    // For the target Layout, pass the animated size as Constraints.
+                    Constraints.fixed(animatedSize.width, animatedSize.height)
+                },
+                animateMotionFrameOfReference = animateMotionFrameOfReference,
+            )
+        )
+
+@ExperimentalSharedTransitionApi
+internal data class BoundsAnimationElement(
+    val lookaheadScope: LookaheadScope,
+    val boundsTransform: BoundsTransform,
+    val resolveMeasureConstraints: (animatedSize: IntSize, constraints: Constraints) -> Constraints,
+    val animateMotionFrameOfReference: Boolean,
+) : ModifierNodeElement<BoundsAnimationModifierNode>() {
+    override fun create(): BoundsAnimationModifierNode {
+        return BoundsAnimationModifierNode(
+            lookaheadScope = lookaheadScope,
+            boundsTransform = boundsTransform,
+            onChooseMeasureConstraints = resolveMeasureConstraints,
+            animateMotionFrameOfReference = animateMotionFrameOfReference,
+        )
+    }
+
+    override fun update(node: BoundsAnimationModifierNode) {
+        node.lookaheadScope = lookaheadScope
+        node.boundsTransform = boundsTransform
+        node.onChooseMeasureConstraints = resolveMeasureConstraints
+        node.animateMotionFrameOfReference = animateMotionFrameOfReference
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "boundsAnimation"
+        properties["lookaheadScope"] = lookaheadScope
+        properties["boundsTransform"] = boundsTransform
+        properties["onChooseMeasureConstraints"] = resolveMeasureConstraints
+        properties["animateMotionFrameOfReference"] = animateMotionFrameOfReference
+    }
+}
+
+/**
+ * [Modifier.Node] implementation that handles the bounds animation with
+ * [ApproachLayoutModifierNode].
+ *
+ * @param lookaheadScope The [LookaheadScope] to animate from.
+ * @param boundsTransform Callback to produce [FiniteAnimationSpec] at every triggered animation
+ * @param onChooseMeasureConstraints Callback to decide whether to measure the Modifier Layout with
+ *   the current animated size value or the incoming constraints. This reflects on the
+ *   [MeasureResult] of this Modifier Layout as well.
+ * @param animateMotionFrameOfReference Whether to include changes under
+ *   [LayoutCoordinates.introducesMotionFrameOfReference] to trigger animations.
+ */
+@ExperimentalSharedTransitionApi
+internal class BoundsAnimationModifierNode(
+    var lookaheadScope: LookaheadScope,
+    var boundsTransform: BoundsTransform,
+    var onChooseMeasureConstraints:
+        (animatedSize: IntSize, constraints: Constraints) -> Constraints,
+    var animateMotionFrameOfReference: Boolean,
+) : ApproachLayoutModifierNode, Modifier.Node() {
+    private val boundsAnimation = BoundsTransformDeferredAnimation()
+
+    override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean {
+        // Update target size, it will serve to know if we expect an approach in progress
+        boundsAnimation.updateTargetSize(lookaheadSize.toSize())
+
+        return !boundsAnimation.isIdle
+    }
+
+    override fun Placeable.PlacementScope.isPlacementApproachInProgress(
+        lookaheadCoordinates: LayoutCoordinates
+    ): Boolean {
+        // Once we can capture size and offset we may also start the animation
+        boundsAnimation.updateTargetOffsetAndAnimate(
+            lookaheadScope = lookaheadScope,
+            placementScope = this,
+            coroutineScope = coroutineScope,
+            includeMotionFrameOfReference = animateMotionFrameOfReference,
+            boundsTransform = boundsTransform,
+        )
+        return !boundsAnimation.isIdle
+    }
+
+    override fun ApproachMeasureScope.approachMeasure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        // The animated value is null on the first frame as we don't get the full bounds
+        // information until placement, so we can safely use the current Size.
+        val fallbackSize =
+            if (boundsAnimation.currentSize.isUnspecified) {
+                // When using Intrinsics, we may get measured before getting the approach check
+                lookaheadSize.toSize()
+            } else {
+                boundsAnimation.currentSize
+            }
+        val animatedSize = (boundsAnimation.value?.size ?: fallbackSize).roundToIntSize()
+
+        val chosenConstraints = onChooseMeasureConstraints(animatedSize, constraints)
+
+        val placeable = measurable.measure(chosenConstraints)
+        return layout(animatedSize.width, animatedSize.height) {
+            val animatedBounds = boundsAnimation.value
+            val positionInScope =
+                with(lookaheadScope) {
+                    coordinates?.let { coordinates ->
+                        lookaheadScopeCoordinates.localPositionOf(
+                            sourceCoordinates = coordinates,
+                            relativeToSource = Offset.Zero,
+                            includeMotionFrameOfReference = animateMotionFrameOfReference
+                        )
+                    }
+                }
+
+            val topLeft =
+                if (animatedBounds != null) {
+                    boundsAnimation.updateCurrentBounds(animatedBounds.topLeft, animatedBounds.size)
+                    animatedBounds.topLeft
+                } else {
+                    boundsAnimation.currentBounds?.topLeft ?: Offset.Zero
+                }
+            val (x, y) = positionInScope?.let { topLeft - it } ?: Offset.Zero
+            placeable.place(x.fastRoundToInt(), y.fastRoundToInt())
+        }
+    }
+}
+
+/** Helper class to keep track of the BoundsAnimation state for [ApproachLayoutModifierNode]. */
+@OptIn(ExperimentalSharedTransitionApi::class)
+internal class BoundsTransformDeferredAnimation {
+    private var animatable: Animatable<Rect, AnimationVector4D>? = null
+
+    private var targetSize: Size = Size.Unspecified
+    private var targetOffset: Offset = Offset.Unspecified
+
+    private var isPending = false
+
+    /**
+     * Captures lookahead size, updates current size for the first pass and marks the animation as
+     * pending.
+     */
+    fun updateTargetSize(size: Size) {
+        if (targetSize.isSpecified && size.roundToIntSize() != targetSize.roundToIntSize()) {
+            // Change in target, animation is pending
+            isPending = true
+        }
+        targetSize = size
+
+        if (currentSize.isUnspecified) {
+            currentSize = size
+        }
+    }
+
+    /**
+     * Captures lookahead position, updates current position for the first pass and marks the
+     * animation as pending.
+     */
+    private fun updateTargetOffset(offset: Offset) {
+        if (targetOffset.isSpecified && offset.round() != targetOffset.round()) {
+            isPending = true
+        }
+        targetOffset = offset
+
+        if (currentPosition.isUnspecified) {
+            currentPosition = offset
+        }
+    }
+
+    // We capture the current bounds parameters individually to avoid unnecessary Rect allocations
+    private var currentPosition: Offset = Offset.Unspecified
+    var currentSize: Size = Size.Unspecified
+
+    val currentBounds: Rect?
+        get() {
+            val size = currentSize
+            val position = currentPosition
+            return if (position.isSpecified && size.isSpecified) {
+                Rect(position, size)
+            } else {
+                null
+            }
+        }
+
+    fun updateCurrentBounds(position: Offset, size: Size) {
+        currentPosition = position
+        currentSize = size
+    }
+
+    val isIdle: Boolean
+        get() = !isPending && animatable?.isRunning != true
+
+    private var animatedValue: Rect? by mutableStateOf(null)
+
+    val value: Rect?
+        get() = if (isIdle) null else animatedValue
+
+    private var directManipulationParents: MutableList<LayoutCoordinates>? = null
+    private var additionalOffset: Offset = Offset.Zero
+
+    fun updateTargetOffsetAndAnimate(
+        lookaheadScope: LookaheadScope,
+        placementScope: Placeable.PlacementScope,
+        coroutineScope: CoroutineScope,
+        includeMotionFrameOfReference: Boolean,
+        boundsTransform: BoundsTransform,
+    ) {
+        placementScope.coordinates?.let { coordinates ->
+            with(lookaheadScope) {
+                val lookaheadScopeCoordinates = placementScope.lookaheadScopeCoordinates
+
+                var delta = Offset.Zero
+                if (!includeMotionFrameOfReference) {
+                    // As the Layout changes, we need to keep track of the accumulated offset up
+                    // the hierarchy tree, to get the proper Offset accounting for scrolling.
+                    val parents = directManipulationParents ?: mutableListOf()
+                    var currentCoords = coordinates
+                    var index = 0
+
+                    // Find the given lookahead coordinates by traversing up the tree
+                    while (currentCoords.toLookaheadCoordinates() != lookaheadScopeCoordinates) {
+                        if (currentCoords.introducesMotionFrameOfReference) {
+                            if (parents.size == index) {
+                                parents.add(currentCoords)
+                                delta += currentCoords.positionInParent()
+                            } else if (parents[index] != currentCoords) {
+                                delta -= parents[index].positionInParent()
+                                parents[index] = currentCoords
+                                delta += currentCoords.positionInParent()
+                            }
+                            index++
+                        }
+                        currentCoords = currentCoords.parentCoordinates ?: break
+                    }
+
+                    for (i in parents.size - 1 downTo index) {
+                        delta -= parents[i].positionInParent()
+                        parents.removeAt(parents.size - 1)
+                    }
+                    directManipulationParents = parents
+                }
+                additionalOffset += delta
+
+                val targetOffset =
+                    lookaheadScopeCoordinates.localLookaheadPositionOf(
+                        sourceCoordinates = coordinates,
+                        includeMotionFrameOfReference = includeMotionFrameOfReference
+                    )
+                updateTargetOffset(targetOffset + additionalOffset)
+
+                animatedValue =
+                    animate(coroutineScope = coroutineScope, boundsTransform = boundsTransform)
+                        .translate(-(additionalOffset))
+            }
+        }
+    }
+
+    private fun animate(
+        coroutineScope: CoroutineScope,
+        boundsTransform: BoundsTransform,
+    ): Rect {
+        if (targetOffset.isSpecified && targetSize.isSpecified) {
+            // Initialize Animatable when possible, we might not use it but we need to have it
+            // instantiated since at the first pass the lookahead information will become the
+            // initial bounds when we actually need an animation.
+            val target = Rect(targetOffset, targetSize)
+            val anim = animatable ?: Animatable(target, Rect.VectorConverter)
+            animatable = anim
+
+            // This check should avoid triggering an animation on the first pass, as there would not
+            // be enough information to have a distinct current and target bounds.
+            if (isPending) {
+                isPending = false
+                coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) {
+                    // Dispatch right away to make sure approach callbacks are accurate on `isIdle`
+                    anim.animateTo(target, boundsTransform.transform(currentBounds!!, target))
+                }
+            }
+        }
+        return animatable?.value ?: Rect.Zero
+    }
+}
+
+@OptIn(ExperimentalSharedTransitionApi::class)
+private val DefaultBoundsTransform = BoundsTransform { _, _ ->
+    spring(
+        dampingRatio = Spring.DampingRatioNoBouncy,
+        stiffness = Spring.StiffnessMediumLow,
+        visibilityThreshold = Rect.VisibilityThreshold
+    )
+}
diff --git a/compose/material3/adaptive/adaptive-layout/api/current.txt b/compose/material3/adaptive/adaptive-layout/api/current.txt
index bd0cb05..8b5adeb 100644
--- a/compose/material3/adaptive/adaptive-layout/api/current.txt
+++ b/compose/material3/adaptive/adaptive-layout/api/current.txt
@@ -41,7 +41,7 @@
   }
 
   public final class ListDetailPaneScaffoldKt {
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
     method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
   }
 
@@ -209,7 +209,7 @@
   }
 
   public final class SupportingPaneScaffoldKt {
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
     method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
   }
 
@@ -229,10 +229,10 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ThreePaneScaffoldDestinationItem<T> {
-    ctor public ThreePaneScaffoldDestinationItem(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? content);
-    method public T? getContent();
+    ctor public ThreePaneScaffoldDestinationItem(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? contentKey);
+    method public T? getContentKey();
     method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getPane();
-    property public final T? content;
+    property public final T? contentKey;
     property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane;
   }
 
diff --git a/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt b/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
index bd0cb05..8b5adeb 100644
--- a/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
+++ b/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
@@ -41,7 +41,7 @@
   }
 
   public final class ListDetailPaneScaffoldKt {
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
     method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
   }
 
@@ -209,7 +209,7 @@
   }
 
   public final class SupportingPaneScaffoldKt {
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
     method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
   }
 
@@ -229,10 +229,10 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ThreePaneScaffoldDestinationItem<T> {
-    ctor public ThreePaneScaffoldDestinationItem(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? content);
-    method public T? getContent();
+    ctor public ThreePaneScaffoldDestinationItem(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? contentKey);
+    method public T? getContentKey();
     method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getPane();
-    property public final T? content;
+    property public final T? contentKey;
     property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane;
   }
 
diff --git a/compose/material3/adaptive/adaptive-layout/build.gradle b/compose/material3/adaptive/adaptive-layout/build.gradle
index 9400e45..f37fbdb 100644
--- a/compose/material3/adaptive/adaptive-layout/build.gradle
+++ b/compose/material3/adaptive/adaptive-layout/build.gradle
@@ -42,14 +42,14 @@
             dependencies {
                 implementation(libs.kotlinStdlib)
                 api(project(":compose:material3:adaptive:adaptive"))
-                api("androidx.compose.animation:animation-core:1.7.0-rc01")
-                api("androidx.compose.ui:ui:1.7.0-rc01")
-                implementation("androidx.compose.animation:animation:1.7.0-rc01")
+                api("androidx.compose.animation:animation-core:1.7.0")
+                api("androidx.compose.ui:ui:1.7.0")
+                implementation("androidx.compose.animation:animation:1.7.0")
                 implementation("androidx.compose.foundation:foundation:1.6.5")
                 implementation("androidx.compose.foundation:foundation-layout:1.6.5")
                 implementation("androidx.compose.ui:ui-geometry:1.6.5")
                 implementation("androidx.compose.ui:ui-util:1.6.5")
-                implementation("androidx.window:window-core:1.3.0-rc01")
+                implementation("androidx.window:window-core:1.3.0")
             }
         }
 
@@ -83,7 +83,7 @@
             dependencies {
                 implementation(project(":compose:material3:material3"))
                 implementation(project(":compose:test-utils"))
-                implementation(project(":window:window-testing"))
+                implementation("androidx.window:window-testing:1.3.0")
                 implementation(libs.junit)
                 implementation(libs.testRunner)
                 implementation(libs.truth)
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt b/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt
index ea19285..2718a66 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt
@@ -32,6 +32,7 @@
 @RunWith(JUnit4::class)
 class PaneScaffoldDirectiveTest {
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateStandardPaneScaffoldDirective_compactWidth() {
         val scaffoldDirective =
             calculatePaneScaffoldDirective(
@@ -46,6 +47,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateStandardPaneScaffoldDirective_mediumWidth() {
         val scaffoldDirective =
             calculatePaneScaffoldDirective(
@@ -60,6 +62,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateStandardPaneScaffoldDirective_expandedWidth() {
         val scaffoldDirective =
             calculatePaneScaffoldDirective(
@@ -74,6 +77,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateStandardPaneScaffoldDirective_tabletop() {
         val scaffoldDirective =
             calculatePaneScaffoldDirective(
@@ -88,6 +92,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateDensePaneScaffoldDirective_compactWidth() {
         val scaffoldDirective =
             calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -102,6 +107,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateDensePaneScaffoldDirective_mediumWidth() {
         val scaffoldDirective =
             calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -116,6 +122,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateDensePaneScaffoldDirective_expandedWidth() {
         val scaffoldDirective =
             calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -130,6 +137,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateDensePaneScaffoldDirective_tabletop() {
         val scaffoldDirective =
             calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -144,6 +152,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateStandardPaneScaffoldDirective_alwaysAvoidHinge() {
         val scaffoldDirective =
             calculatePaneScaffoldDirective(
@@ -158,6 +167,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateStandardPaneScaffoldDirective_avoidOccludingHinge() {
         val scaffoldDirective =
             calculatePaneScaffoldDirective(
@@ -172,6 +182,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateStandardPaneScaffoldDirective_avoidSeparatingHinge() {
         val scaffoldDirective =
             calculatePaneScaffoldDirective(
@@ -186,6 +197,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateStandardPaneScaffoldDirective_neverAvoidHinge() {
         val scaffoldDirective =
             calculatePaneScaffoldDirective(
@@ -200,6 +212,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateDensePaneScaffoldDirective_alwaysAvoidHinge() {
         val scaffoldDirective =
             calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -214,6 +227,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateDensePaneScaffoldDirective_avoidOccludingHinge() {
         val scaffoldDirective =
             calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -228,6 +242,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateDensePaneScaffoldDirective_avoidSeparatingHinge() {
         val scaffoldDirective =
             calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -242,6 +257,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun test_calculateDensePaneScaffoldDirective_neverAvoidHinge() {
         val scaffoldDirective =
             calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
index 5bb4f4c..c25d25c 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
@@ -47,12 +47,10 @@
  * @param extraPane the extra pane of the scaffold, which is supposed to hold any supplementary info
  *   besides the list and the detail panes, for example, a task list or a mini-calendar view of a
  *   mail app. See [ListDetailPaneScaffoldRole.Extra].
- * @param paneExpansionDragHandle the pane expansion drag handle to let users be able to drag to
- *   change pane expansion state. Note that by default this argument will be `null`, and there won't
- *   be a drag handle rendered and users won't be able to drag to change the pane split. You can
- *   provide a [PaneExpansionDragHandle] here as our sample suggests. On the other hand, even if
- *   there's no drag handle, you can still modify [paneExpansionState] directly to apply pane
- *   expansion.
+ * @param paneExpansionDragHandle provide a custom pane expansion drag handle to allow users to
+ *   resize panes and change the pane expansion state by dragging. This is `null` by default, which
+ *   renders no drag handle. Even there's no drag handle, you can still change pane size directly
+ *   via modifying [paneExpansionState].
  * @param paneExpansionState the state object of pane expansion.
  */
 @ExperimentalMaterial3AdaptiveApi
@@ -102,6 +100,11 @@
  * @param extraPane the extra pane of the scaffold, which is supposed to hold any supplementary info
  *   besides the list and the detail panes, for example, a task list or a mini-calendar view of a
  *   mail app. See [ListDetailPaneScaffoldRole.Extra].
+ * @param paneExpansionDragHandle provide a custom pane expansion drag handle to allow users to
+ *   resize panes and change the pane expansion state by dragging. This is `null` by default, which
+ *   renders no drag handle. Even there's no drag handle, you can still change pane size directly
+ *   via modifying [paneExpansionState].
+ * @param paneExpansionState the state object of pane expansion.
  */
 @ExperimentalMaterial3AdaptiveApi
 @Composable
@@ -112,6 +115,9 @@
     detailPane: @Composable ThreePaneScaffoldPaneScope.() -> Unit,
     modifier: Modifier = Modifier,
     extraPane: (@Composable ThreePaneScaffoldPaneScope.() -> Unit)? = null,
+    paneExpansionDragHandle: (@Composable ThreePaneScaffoldScope.(PaneExpansionState) -> Unit)? =
+        null,
+    paneExpansionState: PaneExpansionState = rememberPaneExpansionState(scaffoldState.targetState),
 ) {
     ThreePaneScaffold(
         modifier = modifier.fillMaxSize(),
@@ -120,6 +126,8 @@
         paneOrder = ListDetailPaneScaffoldDefaults.PaneOrder,
         secondaryPane = listPane,
         tertiaryPane = extraPane,
+        paneExpansionDragHandle = paneExpansionDragHandle,
+        paneExpansionState = paneExpansionState,
         primaryPane = detailPane
     )
 }
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt
index d0af5ed..a2a06c7 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:Suppress("DEPRECATED") // Deprecated import WindowWidthSizeClass.
+
 package androidx.compose.material3.adaptive.layout
 
 import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
@@ -27,7 +29,6 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
-import androidx.window.core.layout.WindowWidthSizeClass
 
 /**
  * Calculates the recommended [PaneScaffoldDirective] from a given [WindowAdaptiveInfo]. Use this
@@ -44,6 +45,7 @@
  * @return an [PaneScaffoldDirective] to be used to decide adaptive layout states.
  */
 @ExperimentalMaterial3AdaptiveApi
+@Suppress("DEPRECATION") // WindowWidthSizeClass is deprecated
 fun calculatePaneScaffoldDirective(
     windowAdaptiveInfo: WindowAdaptiveInfo,
     verticalHingePolicy: HingePolicy = HingePolicy.AvoidSeparating
@@ -51,11 +53,11 @@
     val maxHorizontalPartitions: Int
     val horizontalPartitionSpacerSize: Dp
     when (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass) {
-        WindowWidthSizeClass.COMPACT -> {
+        androidx.window.core.layout.WindowWidthSizeClass.COMPACT -> {
             maxHorizontalPartitions = 1
             horizontalPartitionSpacerSize = 0.dp
         }
-        WindowWidthSizeClass.MEDIUM -> {
+        androidx.window.core.layout.WindowWidthSizeClass.MEDIUM -> {
             maxHorizontalPartitions = 1
             horizontalPartitionSpacerSize = 0.dp
         }
@@ -108,12 +110,14 @@
  * @return an [PaneScaffoldDirective] to be used to decide adaptive layout states.
  */
 @ExperimentalMaterial3AdaptiveApi
+@Suppress("DEPRECATION") // WindowWidthSizeClass is deprecated
 fun calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
     windowAdaptiveInfo: WindowAdaptiveInfo,
     verticalHingePolicy: HingePolicy = HingePolicy.AvoidSeparating
 ): PaneScaffoldDirective {
     val isMediumWidth =
-        windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.MEDIUM
+        windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass ==
+            androidx.window.core.layout.WindowWidthSizeClass.MEDIUM
     return with(calculatePaneScaffoldDirective(windowAdaptiveInfo, verticalHingePolicy)) {
         copy(
             maxHorizontalPartitions = if (isMediumWidth) 2 else maxHorizontalPartitions,
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt
index 7b8abec..de8d35d 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt
@@ -41,12 +41,10 @@
  * @param extraPane the extra pane of the scaffold, which is supposed to hold any additional content
  *   besides the main and the supporting panes, for example, a styling panel in a doc app. See
  *   [SupportingPaneScaffoldRole.Extra].
- * @param paneExpansionDragHandle the pane expansion drag handle to let users be able to drag to
- *   change pane expansion state. Note that by default this argument will be `null`, and there won't
- *   be a drag handle rendered and users won't be able to drag to change the pane split. You can
- *   provide a [PaneExpansionDragHandle] here as our sample suggests. On the other hand, even if
- *   there's no drag handle, you can still modify [paneExpansionState] directly to apply pane
- *   expansion.
+ * @param paneExpansionDragHandle provide a custom pane expansion drag handle to allow users to
+ *   resize panes and change the pane expansion state by dragging. This is `null` by default, which
+ *   renders no drag handle. Even there's no drag handle, you can still change pane size directly
+ *   via modifying [paneExpansionState].
  * @param paneExpansionState the state object of pane expansion.
  */
 @ExperimentalMaterial3AdaptiveApi
@@ -95,6 +93,11 @@
  * @param extraPane the extra pane of the scaffold, which is supposed to hold any additional content
  *   besides the main and the supporting panes, for example, a styling panel in a doc app. See
  *   [SupportingPaneScaffoldRole.Extra].
+ * @param paneExpansionDragHandle provide a custom pane expansion drag handle to allow users to
+ *   resize panes and change the pane expansion state by dragging. This is `null` by default, which
+ *   renders no drag handle. Even there's no drag handle, you can still change pane size directly
+ *   via modifying [paneExpansionState].
+ * @param paneExpansionState the state object of pane expansion.
  */
 @ExperimentalMaterial3AdaptiveApi
 @Composable
@@ -105,6 +108,9 @@
     supportingPane: @Composable ThreePaneScaffoldPaneScope.() -> Unit,
     modifier: Modifier = Modifier,
     extraPane: (@Composable ThreePaneScaffoldPaneScope.() -> Unit)? = null,
+    paneExpansionDragHandle: (@Composable ThreePaneScaffoldScope.(PaneExpansionState) -> Unit)? =
+        null,
+    paneExpansionState: PaneExpansionState = rememberPaneExpansionState(scaffoldState.targetState),
 ) {
     ThreePaneScaffold(
         modifier = modifier.fillMaxSize(),
@@ -113,6 +119,8 @@
         paneOrder = SupportingPaneScaffoldDefaults.PaneOrder,
         secondaryPane = supportingPane,
         tertiaryPane = extraPane,
+        paneExpansionDragHandle = paneExpansionDragHandle,
+        paneExpansionState = paneExpansionState,
         primaryPane = mainPane
     )
 }
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldDestinationItem.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldDestinationItem.kt
index 3c0b995..8ebafe4 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldDestinationItem.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldDestinationItem.kt
@@ -22,31 +22,31 @@
  * An item representing a navigation destination in a [ThreePaneScaffold].
  *
  * @param pane the pane destination of the navigation.
- * @param content the optional content, or an id representing the content of the destination. The
- *   type [T] must be storable in a Bundle.
+ * @param contentKey the optional key or id representing the content of the destination. The type
+ *   [T] must be storable in a Bundle.
  */
 @ExperimentalMaterial3AdaptiveApi
 class ThreePaneScaffoldDestinationItem<out T>(
     val pane: ThreePaneScaffoldRole,
-    val content: T? = null,
+    val contentKey: T? = null,
 ) {
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is ThreePaneScaffoldDestinationItem<*>) return false
 
         if (pane != other.pane) return false
-        if (content != other.content) return false
+        if (contentKey != other.contentKey) return false
 
         return true
     }
 
     override fun hashCode(): Int {
         var result = pane.hashCode()
-        result = 31 * result + (content?.hashCode() ?: 0)
+        result = 31 * result + (contentKey?.hashCode() ?: 0)
         return result
     }
 
     override fun toString(): String {
-        return "ThreePaneScaffoldDestinationItem(pane=$pane, content=$content)"
+        return "ThreePaneScaffoldDestinationItem(pane=$pane, contentKey=$contentKey)"
     }
 }
diff --git a/compose/material3/adaptive/adaptive-navigation/api/current.txt b/compose/material3/adaptive/adaptive-navigation/api/current.txt
index a923bcc..ac6ed05 100644
--- a/compose/material3/adaptive/adaptive-navigation/api/current.txt
+++ b/compose/material3/adaptive/adaptive-navigation/api/current.txt
@@ -28,7 +28,7 @@
     method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getScaffoldValue();
     method public boolean isDestinationHistoryAware();
     method public boolean navigateBack(optional String backNavigationBehavior);
-    method public void navigateTo(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? content);
+    method public void navigateTo(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? contentKey);
     method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue peekPreviousScaffoldValue(optional String backNavigationBehavior);
     method public void setDestinationHistoryAware(boolean);
     property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<T>? currentDestination;
@@ -38,9 +38,9 @@
   }
 
   public final class ThreePaneScaffoldNavigatorKt {
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
     method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends T>> initialDestinationHistory);
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
     method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends T>> initialDestinationHistory);
   }
 
diff --git a/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt b/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt
index a923bcc..ac6ed05 100644
--- a/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt
+++ b/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt
@@ -28,7 +28,7 @@
     method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getScaffoldValue();
     method public boolean isDestinationHistoryAware();
     method public boolean navigateBack(optional String backNavigationBehavior);
-    method public void navigateTo(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? content);
+    method public void navigateTo(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? contentKey);
     method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue peekPreviousScaffoldValue(optional String backNavigationBehavior);
     method public void setDestinationHistoryAware(boolean);
     property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<T>? currentDestination;
@@ -38,9 +38,9 @@
   }
 
   public final class ThreePaneScaffoldNavigatorKt {
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
     method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends T>> initialDestinationHistory);
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
     method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends T>> initialDestinationHistory);
   }
 
diff --git a/compose/material3/adaptive/adaptive-navigation/build.gradle b/compose/material3/adaptive/adaptive-navigation/build.gradle
index 67fef66..dbbcdc2 100644
--- a/compose/material3/adaptive/adaptive-navigation/build.gradle
+++ b/compose/material3/adaptive/adaptive-navigation/build.gradle
@@ -78,7 +78,7 @@
             dependencies {
                 implementation(project(":compose:material3:material3"))
                 implementation(project(":compose:test-utils"))
-                implementation(project(":window:window-testing"))
+                implementation("androidx.window:window-testing:1.3.0")
                 implementation(libs.junit)
                 implementation(libs.testRunner)
                 implementation(libs.truth)
diff --git a/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/ListDetailPaneScaffoldNavigatorTest.kt b/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/ListDetailPaneScaffoldNavigatorTest.kt
index 54e2cf5..5296dfd 100644
--- a/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/ListDetailPaneScaffoldNavigatorTest.kt
+++ b/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/ListDetailPaneScaffoldNavigatorTest.kt
@@ -63,7 +63,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(canNavigateBack).isTrue()
         }
     }
@@ -92,7 +92,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(canNavigateBack).isFalse()
         }
     }
@@ -122,7 +122,7 @@
                 .isEqualTo(PaneAdaptedValue.Hidden)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Extra)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(canNavigateBack).isTrue()
         }
     }
@@ -152,7 +152,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Extra)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(canNavigateBack).isTrue()
         }
     }
@@ -177,7 +177,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(canNavigateBack).isTrue()
             scaffoldNavigator.navigateBack()
         }
@@ -187,7 +187,7 @@
                 .isEqualTo(PaneAdaptedValue.Hidden)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.List)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
             assertThat(canNavigateBack).isFalse()
         }
     }
@@ -211,7 +211,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(scaffoldNavigator.canNavigateBack()).isFalse()
         }
     }
@@ -237,7 +237,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(scaffoldNavigator.canNavigateBack(BackNavigationBehavior.PopLatest)).isTrue()
             scaffoldNavigator.navigateBack(BackNavigationBehavior.PopLatest)
         }
@@ -247,7 +247,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.List)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
         }
     }
 
@@ -271,7 +271,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(1)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(1)
             assertThat(
                     scaffoldNavigator.canNavigateBack(
                         BackNavigationBehavior.PopUntilCurrentDestinationChange
@@ -284,7 +284,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.List)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
         }
     }
 
@@ -307,7 +307,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(1)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(1)
             assertThat(
                     scaffoldNavigator.canNavigateBack(
                         BackNavigationBehavior.PopUntilCurrentDestinationChange
@@ -337,7 +337,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(1)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(1)
             assertThat(
                     scaffoldNavigator.canNavigateBack(BackNavigationBehavior.PopUntilContentChange)
                 )
@@ -348,7 +348,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
         }
     }
 
@@ -372,7 +372,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Extra)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(
                     scaffoldNavigator.canNavigateBack(BackNavigationBehavior.PopUntilContentChange)
                 )
@@ -383,7 +383,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
         }
     }
 
@@ -406,7 +406,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(
                     scaffoldNavigator.canNavigateBack(BackNavigationBehavior.PopUntilContentChange)
                 )
@@ -439,7 +439,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.List)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
             scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail, 0)
         }
 
@@ -451,7 +451,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             scaffoldNavigator.navigateBack()
         }
 
@@ -463,7 +463,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.List)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
         }
     }
 
@@ -492,7 +492,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.List)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
             scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail, 0)
         }
 
@@ -504,7 +504,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             scaffoldNavigator.navigateBack()
         }
 
@@ -516,7 +516,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Extra)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
         }
     }
 
@@ -664,7 +664,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             // Switches to dual pane
             mockCurrentScaffoldDirective.value = MockDualPaneScaffoldDirective
         }
@@ -673,7 +673,7 @@
             assertThat(scaffoldNavigator.canNavigateBack()).isFalse()
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(ListDetailPaneScaffoldRole.Detail)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
         }
     }
 }
diff --git a/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/SupportingPaneScaffoldNavigatorTest.kt b/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/SupportingPaneScaffoldNavigatorTest.kt
index d8c1561..ef9c1bf 100644
--- a/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/SupportingPaneScaffoldNavigatorTest.kt
+++ b/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/SupportingPaneScaffoldNavigatorTest.kt
@@ -63,7 +63,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(canNavigateBack).isTrue()
         }
     }
@@ -92,7 +92,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Main)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
             assertThat(canNavigateBack).isFalse()
         }
     }
@@ -123,7 +123,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Extra, 1)
         }
 
@@ -132,7 +132,7 @@
                 .isEqualTo(PaneAdaptedValue.Hidden)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Extra)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(1)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(1)
             assertThat(canNavigateBack).isTrue()
         }
     }
@@ -163,7 +163,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Extra, 1)
         }
 
@@ -172,7 +172,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Extra)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(1)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(1)
             assertThat(canNavigateBack).isTrue()
         }
     }
@@ -199,7 +199,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(canNavigateBack).isTrue()
             scaffoldNavigator.navigateBack()
         }
@@ -209,7 +209,7 @@
                 .isEqualTo(PaneAdaptedValue.Hidden)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Main)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
             assertThat(canNavigateBack).isFalse()
         }
     }
@@ -236,7 +236,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Main)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
             assertThat(scaffoldNavigator.canNavigateBack()).isFalse()
         }
     }
@@ -265,7 +265,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Main)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
             assertThat(scaffoldNavigator.canNavigateBack(BackNavigationBehavior.PopLatest)).isTrue()
             scaffoldNavigator.navigateBack(BackNavigationBehavior.PopLatest)
         }
@@ -275,7 +275,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
         }
     }
 
@@ -305,7 +305,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(1)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(1)
             assertThat(
                     scaffoldNavigator.canNavigateBack(
                         BackNavigationBehavior.PopUntilCurrentDestinationChange
@@ -318,7 +318,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Main)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
         }
     }
 
@@ -341,7 +341,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Main)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(1)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(1)
             assertThat(
                     scaffoldNavigator.canNavigateBack(
                         BackNavigationBehavior.PopUntilCurrentDestinationChange
@@ -377,7 +377,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(1)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(1)
             assertThat(
                     scaffoldNavigator.canNavigateBack(BackNavigationBehavior.PopUntilContentChange)
                 )
@@ -388,7 +388,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
         }
     }
 
@@ -415,7 +415,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Extra)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(
                     scaffoldNavigator.canNavigateBack(BackNavigationBehavior.PopUntilContentChange)
                 )
@@ -426,7 +426,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
         }
     }
 
@@ -452,7 +452,7 @@
         composeRule.runOnIdle {
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             assertThat(
                     scaffoldNavigator.canNavigateBack(BackNavigationBehavior.PopUntilContentChange)
                 )
@@ -488,7 +488,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Main)
         }
 
@@ -500,7 +500,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Main)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
             scaffoldNavigator.navigateBack()
         }
 
@@ -512,7 +512,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
         }
     }
 
@@ -544,7 +544,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Supporting)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(0)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(0)
             scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Main)
         }
 
@@ -556,7 +556,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Main)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
             scaffoldNavigator.navigateBack()
         }
 
@@ -568,7 +568,7 @@
             )
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Extra)
-            assertThat(scaffoldNavigator.currentDestination?.content).isEqualTo(1)
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isEqualTo(1)
         }
     }
 
@@ -728,7 +728,7 @@
                 .isEqualTo(PaneAdaptedValue.Expanded)
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Main)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
             // Switches to dual pane
             mockCurrentScaffoldDirective.value = MockDualPaneScaffoldDirective
         }
@@ -737,7 +737,7 @@
             assertThat(scaffoldNavigator.canNavigateBack()).isFalse()
             assertThat(scaffoldNavigator.currentDestination?.pane)
                 .isEqualTo(SupportingPaneScaffoldRole.Main)
-            assertThat(scaffoldNavigator.currentDestination?.content).isNull()
+            assertThat(scaffoldNavigator.currentDestination?.contentKey).isNull()
         }
     }
 }
diff --git a/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/BackNavigationBehavior.kt b/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/BackNavigationBehavior.kt
index e3ba0c4..66f554d7 100644
--- a/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/BackNavigationBehavior.kt
+++ b/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/BackNavigationBehavior.kt
@@ -55,7 +55,7 @@
         /**
          * Pop destinations from the backstack until there is a content change.
          *
-         * A "content change" is defined as either a change in the content of the current
+         * A "content change" is defined as either a change in the `contentKey` of the current
          * [ThreePaneScaffoldDestinationItem], or a change in the scaffold value (similar to
          * [PopUntilScaffoldValueChange]).
          */
diff --git a/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldNavigator.kt b/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldNavigator.kt
index 81a8de2..42378e4 100644
--- a/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldNavigator.kt
+++ b/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldNavigator.kt
@@ -59,9 +59,9 @@
  * and the default implementation to get better understanding and address the intricacies of
  * navigation in an adaptive scenario.
  *
- * @param T the type representing the content, or id of the content, for a navigation destination.
- *   This type must be storable in a Bundle. Used to customize navigation behavior (for example,
- *   [BackNavigationBehavior]). If this customization is unneeded, you can pass [Nothing].
+ * @param T the type representing the content key/id for a navigation destination. This type must be
+ *   storable in a Bundle. Used to customize navigation behavior (for example,
+ *   [BackNavigationBehavior]). If this customization is unneeded, you can pass [Any].
  */
 @ExperimentalMaterial3AdaptiveApi
 @Stable
@@ -116,10 +116,9 @@
      * pane currently being used.
      *
      * @param pane the new destination pane.
-     * @param content the optional content, or an id representing the content of the new
-     *   destination.
+     * @param contentKey the optional key or id representing the content of the new destination.
      */
-    fun navigateTo(pane: ThreePaneScaffoldRole, content: T? = null)
+    fun navigateTo(pane: ThreePaneScaffoldRole, contentKey: T? = null)
 
     /**
      * Returns `true` if there is a previous destination to navigate back to.
@@ -157,9 +156,9 @@
  * default navigator is supposed to be used independently from any navigation frameworks and handles
  * the navigation purely inside the [ListDetailPaneScaffold].
  *
- * @param T the type representing the content, or id of the content, for a navigation destination.
- *   This type must be storable in a Bundle. Used to customize navigation behavior (for example,
- *   [BackNavigationBehavior]). If this customization is unneeded, you can pass [Nothing].
+ * @param T the type representing the content key/id for a navigation destination. This type must be
+ *   storable in a Bundle. Used to customize navigation behavior (for example,
+ *   [BackNavigationBehavior]). If this customization is unneeded, you can pass [Any].
  * @param scaffoldDirective the current layout directives to follow. The default value will be
  *   calculated with [calculatePaneScaffoldDirective] using
  *   [WindowAdaptiveInfo][androidx.compose.material3.adaptive.WindowAdaptiveInfo] retrieved from the
@@ -212,8 +211,8 @@
     adaptStrategies: ThreePaneScaffoldAdaptStrategies =
         ListDetailPaneScaffoldDefaults.adaptStrategies(),
     isDestinationHistoryAware: Boolean = true,
-): ThreePaneScaffoldNavigator<Nothing> =
-    rememberListDetailPaneScaffoldNavigator<Nothing>(
+): ThreePaneScaffoldNavigator<Any> =
+    rememberListDetailPaneScaffoldNavigator<Any>(
         scaffoldDirective,
         adaptStrategies,
         isDestinationHistoryAware,
@@ -225,9 +224,9 @@
  * default navigator is supposed to be used independently from any navigation frameworks and handles
  * the navigation purely inside the [SupportingPaneScaffold].
  *
- * @param T the type representing the content, or id of the content, for a navigation destination.
- *   This type must be storable in a Bundle. Used to customize navigation behavior (for example,
- *   [BackNavigationBehavior]). If this customization is unneeded, you can pass [Nothing].
+ * @param T the type representing the content key/id for a navigation destination. This type must be
+ *   storable in a Bundle. Used to customize navigation behavior (for example,
+ *   [BackNavigationBehavior]). If this customization is unneeded, you can pass [Any].
  * @param scaffoldDirective the current layout directives to follow. The default value will be
  *   calculated with [calculatePaneScaffoldDirective] using
  *   [WindowAdaptiveInfo][androidx.compose.material3.adaptive.WindowAdaptiveInfo] retrieved from the
@@ -280,8 +279,8 @@
     adaptStrategies: ThreePaneScaffoldAdaptStrategies =
         SupportingPaneScaffoldDefaults.adaptStrategies(),
     isDestinationHistoryAware: Boolean = true,
-): ThreePaneScaffoldNavigator<Nothing> =
-    rememberSupportingPaneScaffoldNavigator<Nothing>(
+): ThreePaneScaffoldNavigator<Any> =
+    rememberSupportingPaneScaffoldNavigator<Any>(
         scaffoldDirective,
         adaptStrategies,
         isDestinationHistoryAware,
@@ -349,8 +348,8 @@
         return if (index == -1) scaffoldValue else calculateScaffoldValue(index)
     }
 
-    override fun navigateTo(pane: ThreePaneScaffoldRole, content: T?) {
-        destinationHistory.add(ThreePaneScaffoldDestinationItem(pane, content))
+    override fun navigateTo(pane: ThreePaneScaffoldRole, contentKey: T?) {
+        destinationHistory.add(ThreePaneScaffoldDestinationItem(pane, contentKey))
     }
 
     override fun canNavigateBack(backNavigationBehavior: BackNavigationBehavior): Boolean =
@@ -392,8 +391,8 @@
                 }
             BackNavigationBehavior.PopUntilContentChange ->
                 for (previousDestinationIndex in destinationHistory.lastIndex - 1 downTo 0) {
-                    val content = destinationHistory[previousDestinationIndex].content
-                    if (content != currentDestination?.content) {
+                    val contentKey = destinationHistory[previousDestinationIndex].contentKey
+                    if (contentKey != currentDestination?.contentKey) {
                         return previousDestinationIndex
                     }
                     // A scaffold value change also counts as a content change.
@@ -461,12 +460,12 @@
 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
 internal fun <T> destinationItemSaver(): Saver<ThreePaneScaffoldDestinationItem<T>, Any> =
     listSaver(
-        save = { listOf(it.pane, it.content) },
+        save = { listOf(it.pane, it.contentKey) },
         restore = {
             @Suppress("UNCHECKED_CAST")
             (ThreePaneScaffoldDestinationItem(
                 pane = it[0] as ThreePaneScaffoldRole,
-                content = it[1] as T?
+                contentKey = it[1] as T?
             ))
         }
     )
diff --git a/compose/material3/adaptive/adaptive/build.gradle b/compose/material3/adaptive/adaptive/build.gradle
index 7ab026e..1cb594e 100644
--- a/compose/material3/adaptive/adaptive/build.gradle
+++ b/compose/material3/adaptive/adaptive/build.gradle
@@ -43,7 +43,7 @@
                 implementation(libs.kotlinStdlib)
                 api("androidx.compose.foundation:foundation:1.6.5")
                 api("androidx.compose.ui:ui-geometry:1.6.5")
-                api("androidx.window:window-core:1.3.0-rc01")
+                api("androidx.window:window-core:1.3.0")
             }
         }
 
@@ -63,7 +63,7 @@
             dependencies {
                 api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
-                api("androidx.window:window:1.3.0-rc01")
+                api("androidx.window:window:1.3.0")
             }
         }
 
@@ -78,7 +78,7 @@
             dependencies {
                 implementation(project(":compose:material3:material3"))
                 implementation(project(":compose:test-utils"))
-                implementation(project(":window:window-testing"))
+                implementation("androidx.window:window-testing:1.3.0")
                 implementation(libs.junit)
                 implementation(libs.testRunner)
                 implementation(libs.truth)
diff --git a/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectWindowSizeAsStateTest.kt b/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectWindowSizeAsStateTest.kt
deleted file mode 100644
index bf9047b..0000000
--- a/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectWindowSizeAsStateTest.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material3.adaptive
-
-import android.app.Activity
-import android.content.Context
-import android.content.res.Configuration
-import android.graphics.Rect
-import androidx.annotation.UiContext
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.State
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.unit.IntSize
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import androidx.window.layout.WindowMetrics
-import androidx.window.layout.WindowMetricsCalculator
-import androidx.window.layout.WindowMetricsCalculatorDecorator
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class CollectWindowSizeAsStateTest {
-    @get:Rule val rule = createComposeRule()
-
-    @Test
-    fun test_collectWindowSizeAsState() {
-        var actualWindowSize: IntSize = IntSize.Zero
-
-        val mockWindowSize = mutableStateOf(MockWindowSize1)
-        WindowMetricsCalculator.overrideDecorator(
-            MockWindowMetricsCalculatorDecorator(mockWindowSize)
-        )
-
-        rule.setContent {
-            val testConfiguration = Configuration(LocalConfiguration.current)
-            testConfiguration.screenWidthDp = mockWindowSize.value.width
-            testConfiguration.screenHeightDp = mockWindowSize.value.height
-            CompositionLocalProvider(LocalConfiguration provides testConfiguration) {
-                actualWindowSize = currentWindowSize()
-            }
-        }
-
-        rule.runOnIdle { assertThat(actualWindowSize).isEqualTo(MockWindowSize1) }
-
-        mockWindowSize.value = MockWindowSize2
-
-        rule.runOnIdle { assertThat(actualWindowSize).isEqualTo(MockWindowSize2) }
-    }
-
-    companion object {
-        val MockWindowSize1 = IntSize(1000, 600)
-        val MockWindowSize2 = IntSize(800, 400)
-    }
-}
-
-internal class MockWindowMetricsCalculatorDecorator(private val mockWindowSize: State<IntSize>) :
-    WindowMetricsCalculatorDecorator {
-    override fun decorate(calculator: WindowMetricsCalculator): WindowMetricsCalculator {
-        return MockWindowMetricsCalculator(mockWindowSize)
-    }
-}
-
-internal class MockWindowMetricsCalculator(private val mockWindowSize: State<IntSize>) :
-    WindowMetricsCalculator {
-    override fun computeCurrentWindowMetrics(activity: Activity): WindowMetrics {
-        return WindowMetrics(
-            Rect(0, 0, mockWindowSize.value.width, mockWindowSize.value.height),
-            density = 1f
-        )
-    }
-
-    override fun computeMaximumWindowMetrics(activity: Activity): WindowMetrics {
-        return computeCurrentWindowMetrics(activity)
-    }
-
-    override fun computeCurrentWindowMetrics(@UiContext context: Context): WindowMetrics {
-        return WindowMetrics(
-            Rect(0, 0, mockWindowSize.value.width, mockWindowSize.value.height),
-            density = 1f
-        )
-    }
-}
diff --git a/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CurrentWindowAdaptiveInfoTest.kt b/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CurrentWindowAdaptiveInfoTest.kt
deleted file mode 100644
index 8ef9012..0000000
--- a/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CurrentWindowAdaptiveInfoTest.kt
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material3.adaptive
-
-import android.content.res.Configuration
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.toSize
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import androidx.window.core.layout.WindowSizeClass
-import androidx.window.layout.FoldingFeature
-import androidx.window.layout.WindowLayoutInfo
-import androidx.window.layout.WindowMetricsCalculator
-import androidx.window.testing.layout.WindowLayoutInfoPublisherRule
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.RuleChain
-import org.junit.rules.TestRule
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class CurrentWindowAdaptiveInfoTest {
-    private val composeRule = createComposeRule()
-    private val layoutInfoRule = WindowLayoutInfoPublisherRule()
-
-    @get:Rule val testRule: TestRule
-
-    init {
-        testRule = RuleChain.outerRule(layoutInfoRule).around(composeRule)
-    }
-
-    @Test
-    fun test_currentWindowAdaptiveInfo() {
-        lateinit var actualAdaptiveInfo: WindowAdaptiveInfo
-        val mockWindowSize = mutableStateOf(MockWindowSize1)
-        WindowMetricsCalculator.overrideDecorator(
-            MockWindowMetricsCalculatorDecorator(mockWindowSize)
-        )
-
-        composeRule.setContent {
-            val testConfiguration = Configuration(LocalConfiguration.current)
-            testConfiguration.screenWidthDp = mockWindowSize.value.width
-            testConfiguration.screenHeightDp = mockWindowSize.value.height
-            CompositionLocalProvider(
-                LocalDensity provides MockDensity,
-                LocalConfiguration provides testConfiguration
-            ) {
-                actualAdaptiveInfo = currentWindowAdaptiveInfo()
-            }
-        }
-
-        layoutInfoRule.overrideWindowLayoutInfo(WindowLayoutInfo(MockFoldingFeatures1))
-
-        composeRule.runOnIdle {
-            val mockSize = with(MockDensity) { MockWindowSize1.toSize().toDpSize() }
-            assertThat(actualAdaptiveInfo.windowSizeClass)
-                .isEqualTo(WindowSizeClass.compute(mockSize.width.value, mockSize.height.value))
-            assertThat(actualAdaptiveInfo.windowPosture)
-                .isEqualTo(calculatePosture(MockFoldingFeatures1))
-        }
-
-        layoutInfoRule.overrideWindowLayoutInfo(WindowLayoutInfo(MockFoldingFeatures2))
-        mockWindowSize.value = MockWindowSize2
-
-        composeRule.runOnIdle {
-            val mockSize = with(MockDensity) { MockWindowSize2.toSize().toDpSize() }
-            assertThat(actualAdaptiveInfo.windowSizeClass)
-                .isEqualTo(WindowSizeClass.compute(mockSize.width.value, mockSize.height.value))
-            assertThat(actualAdaptiveInfo.windowPosture)
-                .isEqualTo(calculatePosture(MockFoldingFeatures2))
-        }
-    }
-
-    companion object {
-        private val MockFoldingFeatures1 =
-            listOf(
-                MockFoldingFeature(orientation = FoldingFeature.Orientation.HORIZONTAL),
-                MockFoldingFeature(orientation = FoldingFeature.Orientation.VERTICAL),
-                MockFoldingFeature(orientation = FoldingFeature.Orientation.HORIZONTAL)
-            )
-
-        private val MockFoldingFeatures2 =
-            listOf(
-                MockFoldingFeature(
-                    isSeparating = false,
-                    orientation = FoldingFeature.Orientation.HORIZONTAL,
-                    state = FoldingFeature.State.FLAT
-                ),
-            )
-
-        private val MockWindowSize1 = IntSize(400, 800)
-        private val MockWindowSize2 = IntSize(800, 400)
-
-        private val MockDensity = Density(1f, 1f)
-    }
-}
diff --git a/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidWindowAdaptiveInfo.android.kt b/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidWindowAdaptiveInfo.android.kt
index 9781752..f47f656 100644
--- a/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidWindowAdaptiveInfo.android.kt
+++ b/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidWindowAdaptiveInfo.android.kt
@@ -33,6 +33,7 @@
 
 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
 @Composable
+@Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
 actual fun currentWindowAdaptiveInfo(): WindowAdaptiveInfo {
     val windowSize = with(LocalDensity.current) { currentWindowSize().toSize().toDpSize() }
     return WindowAdaptiveInfo(
diff --git a/compose/material3/adaptive/samples/src/main/java/androidx/compose/material3/adaptive/samples/ThreePaneScaffoldSample.kt b/compose/material3/adaptive/samples/src/main/java/androidx/compose/material3/adaptive/samples/ThreePaneScaffoldSample.kt
index 77e13de..772978e 100644
--- a/compose/material3/adaptive/samples/src/main/java/androidx/compose/material3/adaptive/samples/ThreePaneScaffoldSample.kt
+++ b/compose/material3/adaptive/samples/src/main/java/androidx/compose/material3/adaptive/samples/ThreePaneScaffoldSample.kt
@@ -290,7 +290,7 @@
         }
         composable(listDetailRoute) {
             val listScrollState = rememberScrollState()
-            val selectedItem = scaffoldNavigator.currentDestination?.content
+            val selectedItem = scaffoldNavigator.currentDestination?.contentKey
 
             // Back behavior can be customized based on the scaffold's layout.
             // In this example, back navigation goes item-by-item when both
@@ -328,7 +328,7 @@
                                                 if (item != selectedItem) {
                                                     scaffoldNavigator.navigateTo(
                                                         pane = ListDetailPaneScaffoldRole.Detail,
-                                                        content = item,
+                                                        contentKey = item,
                                                     )
                                                 }
                                             }
diff --git a/compose/material3/material3-adaptive-navigation-suite/build.gradle b/compose/material3/material3-adaptive-navigation-suite/build.gradle
index 1c8f9ff..6d42780 100644
--- a/compose/material3/material3-adaptive-navigation-suite/build.gradle
+++ b/compose/material3/material3-adaptive-navigation-suite/build.gradle
@@ -44,7 +44,7 @@
                 api(project(":compose:material3:material3"))
                 api(project(":compose:material3:adaptive:adaptive"))
                 implementation("androidx.compose.ui:ui-util:1.6.0")
-                implementation("androidx.window:window-core:1.3.0-beta02")
+                implementation("androidx.window:window-core:1.3.0")
             }
         }
 
@@ -77,7 +77,7 @@
             dependsOn(commonTest)
             dependencies {
                 implementation(project(":compose:test-utils"))
-                implementation(project(":window:window-testing"))
+                implementation("androidx.window:window-testing:1.3.0")
                 implementation(libs.junit)
                 implementation(libs.testRunner)
                 implementation(libs.truth)
diff --git a/compose/material3/material3-adaptive-navigation-suite/samples/build.gradle b/compose/material3/material3-adaptive-navigation-suite/samples/build.gradle
index d65e6a8..57e571d 100644
--- a/compose/material3/material3-adaptive-navigation-suite/samples/build.gradle
+++ b/compose/material3/material3-adaptive-navigation-suite/samples/build.gradle
@@ -44,7 +44,7 @@
     implementation(project(":compose:material3:material3-adaptive-navigation-suite"))
     implementation("androidx.compose.ui:ui-util:1.6.0-rc01")
     implementation("androidx.compose.ui:ui-tooling-preview:1.4.1")
-    implementation(project(":window:window-core"))
+    implementation("androidx.window:window-core:1.3.0")
 
     debugImplementation("androidx.compose.ui:ui-tooling:1.4.1")
 }
diff --git a/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt b/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt
index df28658..7d112c9 100644
--- a/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:Suppress("DEPRECATION") // Suppress for WindowWidthSizeClass
+
 package androidx.compose.material3.adaptive.navigationsuite.samples
 
 import androidx.annotation.Sampled
@@ -76,6 +78,7 @@
 @Preview
 @Sampled
 @Composable
+@Suppress("DEPRECATION") // WindowWidthSizeClass is deprecated
 fun NavigationSuiteScaffoldCustomConfigSample() {
     var selectedItem by remember { mutableIntStateOf(0) }
     val navItems = listOf("Songs", "Artists", "Playlists")
diff --git a/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt b/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
index 1eb9fa1..a68b7ddc 100644
--- a/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
@@ -30,6 +30,7 @@
 class NavigationSuiteScaffoldTest {
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun navigationLayoutTypeTest_compactWidth_compactHeight() {
         val mockAdaptiveInfo =
             createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(400f, 400f))
@@ -39,6 +40,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun navigationLayoutTypeTest_compactWidth_mediumHeight() {
         val mockAdaptiveInfo =
             createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(400f, 800f))
@@ -48,6 +50,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun navigationLayoutTypeTest_compactWidth_expandedHeight() {
         val mockAdaptiveInfo =
             createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(400f, 1000f))
@@ -57,6 +60,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun navigationLayoutTypeTest_mediumWidth_compactHeight() {
         val mockAdaptiveInfo =
             createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(800f, 400f))
@@ -66,6 +70,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun navigationLayoutTypeTest_mediumWidth_mediumHeight() {
         val mockAdaptiveInfo =
             createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(800f, 800f))
@@ -75,6 +80,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun navigationLayoutTypeTest_mediumWidth_expandedHeight() {
         val mockAdaptiveInfo =
             createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(800f, 1000f))
@@ -84,6 +90,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun navigationLayoutTypeTest_expandedWidth_compactHeight() {
         val mockAdaptiveInfo =
             createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(1000f, 400f))
@@ -93,6 +100,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun navigationLayoutTypeTest_expandedWidth_mediumHeight() {
         val mockAdaptiveInfo =
             createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(1000f, 800f))
@@ -102,6 +110,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun navigationLayoutTypeTest_expandedWidth_expandedHeight() {
         val mockAdaptiveInfo =
             createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(1000f, 1000f))
@@ -111,6 +120,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun navigationLayoutTypeTest_tableTop() {
         val mockAdaptiveInfo =
             createMockAdaptiveInfo(
@@ -123,6 +133,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
     fun navigationLayoutTypeTest_tableTop_expandedWidth() {
         val mockAdaptiveInfo =
             createMockAdaptiveInfo(
diff --git a/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt b/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
index 5e96cd5..40758e0 100644
--- a/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:Suppress("DEPRECATION") // Suppress for imports of WindowWidthSizeClass
+
 package androidx.compose.material3.adaptive.navigationsuite
 
 import androidx.compose.foundation.interaction.Interaction
@@ -407,6 +409,7 @@
      * @param adaptiveInfo the provided [WindowAdaptiveInfo]
      * @see NavigationSuiteScaffold
      */
+    @Suppress("DEPRECATION") // WindowWidthSizeClass deprecated
     fun calculateFromAdaptiveInfo(adaptiveInfo: WindowAdaptiveInfo): NavigationSuiteType {
         return with(adaptiveInfo) {
             if (
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/NestedScrollingBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/NestedScrollingBenchmark.kt
index 9c909f6..dfd58d7 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/NestedScrollingBenchmark.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/NestedScrollingBenchmark.kt
@@ -34,6 +34,9 @@
 import androidx.test.filters.LargeTest
 import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
 import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -45,6 +48,7 @@
 
     private val nestedScrollingCaseFactory = { NestedScrollingTestCase() }
 
+    @Ignore("b/362302352")
     @Test
     fun nested_scroll_propagation() {
         benchmarkRule.runBenchmarkFor(nestedScrollingCaseFactory) {
@@ -136,12 +140,12 @@
     }
 
     fun assertPostToggle() {
-        assert(collectedDeltasOuter != Offset.Zero)
-        assert(collectedDeltasMiddle != Offset.Zero)
-        assert(collectedVelocityOuter != Velocity.Zero)
-        assert(collectedVelocityMiddle != Velocity.Zero)
+        assertNotEquals(collectedDeltasOuter, Offset.Zero)
+        assertNotEquals(collectedDeltasMiddle, Offset.Zero)
+        assertNotEquals(collectedVelocityOuter, Velocity.Zero)
+        assertNotEquals(collectedVelocityMiddle, Velocity.Zero)
 
-        assert(collectedDeltasOuter == scrollResult)
-        assert(collectedVelocityOuter == velocityResult)
+        assertEquals(scrollResult, collectedDeltasOuter)
+        assertEquals(velocityResult, collectedVelocityOuter)
     }
 }
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/VelocityTrackerBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/VelocityTrackerBenchmark.kt
index d78ffcb..a0e4339 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/VelocityTrackerBenchmark.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/VelocityTrackerBenchmark.kt
@@ -19,6 +19,7 @@
 import androidx.compose.ui.input.pointer.util.VelocityTracker1D
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import org.junit.Assert.assertTrue
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -95,7 +96,7 @@
             velocityTracker.addDataPoint(dataPoint.timeMillis, dataPoint.motionValue)
         }
 
-        benchmarkRule.measureRepeated { assert(velocityTracker.calculateVelocity() != 0f) }
+        benchmarkRule.measureRepeated { assertTrue(velocityTracker.calculateVelocity() != 0f) }
     }
 
     companion object {
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
index 223da87..38f3d5c 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
@@ -2557,8 +2557,9 @@
             // Verify initial offset, should be the same values for the "excluded" offset
             positionToExcludedArray.forEachIndexed { index, (position, excluded) ->
                 // Rounding to avoid -0.0f
-                assertEquals((index * boxSizePx).fastRoundToInt(), position.y.fastRoundToInt())
-                assertEquals((index * boxSizePx).fastRoundToInt(), excluded.y.fastRoundToInt())
+                val expected = (index * boxSizePx).fastRoundToInt()
+                assertEquals("At index: $index", expected, position.y.fastRoundToInt())
+                assertEquals("At index: $index", expected, excluded.y.fastRoundToInt())
             }
 
             // Scroll to the end
@@ -2633,8 +2634,9 @@
             // Verify initial offset, should be the same values for the "excluded" offset
             positionToExcludedArray.forEachIndexed { index, (position, excluded) ->
                 // Rounding to avoid -0.0f
-                assertEquals((index * boxSizePx).fastRoundToInt(), position.y.fastRoundToInt())
-                assertEquals((index * boxSizePx).fastRoundToInt(), excluded.y.fastRoundToInt())
+                val expected = (index * boxSizePx).fastRoundToInt()
+                assertEquals("At index: $index", expected, position.y.fastRoundToInt())
+                assertEquals("At index: $index", expected, excluded.y.fastRoundToInt())
             }
 
             // Scroll to the end
@@ -3102,6 +3104,134 @@
             assertEquals(0, lookingAheadPositionExcludingDmp.y.fastRoundToInt())
         }
 
+    @Test
+    fun testLookaheadAndApproachCoordinatesAreConsistentOnFirstPass_usingAlign() =
+        with(rule.density) {
+            val rootSizePx = 300
+            val alignmentOffsetPx = IntOffset(0, 100)
+
+            // Both positions are expected to be from lookahead coordinates
+            var lookaheadPassPosition = Offset.Unspecified
+            var approachPassPosition = Offset.Unspecified
+
+            rule.setContent {
+                Box(Modifier.size(rootSizePx.toDp())) {
+                    LookaheadScope {
+                        Box(Modifier.align { _, _, _ -> alignmentOffsetPx }.fillMaxWidth()) {
+                            // Capture lookahead coordinates from Lookahead and Approach pass.
+                            Box(
+                                Modifier.onLookaheadPassCoordinates(this@LookaheadScope) {
+                                        lookaheadScopeCoordinates,
+                                        coordinates ->
+                                        lookaheadPassPosition =
+                                            lookaheadScopeCoordinates.localPositionOf(coordinates)
+                                    }
+                                    .onApproachPassCoordinates(this@LookaheadScope) {
+                                        lookaheadScopeCoordinates,
+                                        coordinates ->
+                                        approachPassPosition =
+                                            lookaheadScopeCoordinates.localLookaheadPositionOf(
+                                                coordinates
+                                            )
+                                    }
+                            )
+                        }
+                    }
+                }
+            }
+            rule.waitForIdle()
+
+            // Assert both positions are equal on the first pass.
+            assertEquals(alignmentOffsetPx, lookaheadPassPosition.round())
+            assertEquals(alignmentOffsetPx, approachPassPosition.round())
+        }
+
+    @Test
+    fun testLookaheadAndApproachCoordinatesAreConsistentOnFirstPass_usingOffset() =
+        with(rule.density) {
+            val rootSizePx = 300
+            val alignmentOffsetPx = IntOffset(0, 100)
+
+            // Both positions are expected to be from lookahead coordinates
+            var lookaheadPassPosition = Offset.Unspecified
+            var approachPassPosition = Offset.Unspecified
+
+            rule.setContent {
+                Box(Modifier.size(rootSizePx.toDp())) {
+                    LookaheadScope {
+                        Box(Modifier.offset { alignmentOffsetPx }.fillMaxWidth()) {
+                            // Capture lookahead coordinates from Lookahead and Approach pass.
+                            Box(
+                                Modifier.onLookaheadPassCoordinates(this@LookaheadScope) {
+                                        lookaheadScopeCoordinates,
+                                        coordinates ->
+                                        lookaheadPassPosition =
+                                            lookaheadScopeCoordinates.localPositionOf(coordinates)
+                                    }
+                                    .onApproachPassCoordinates(this@LookaheadScope) {
+                                        lookaheadScopeCoordinates,
+                                        coordinates ->
+                                        approachPassPosition =
+                                            lookaheadScopeCoordinates.localLookaheadPositionOf(
+                                                coordinates
+                                            )
+                                    }
+                            )
+                        }
+                    }
+                }
+            }
+            rule.waitForIdle()
+
+            // Assert both positions are equal on the first pass.
+            assertEquals(alignmentOffsetPx, lookaheadPassPosition.round())
+            assertEquals(alignmentOffsetPx, approachPassPosition.round())
+        }
+
+    /** Capture LookaheadScope coordinates during the Lookahead pass. */
+    private fun Modifier.onLookaheadPassCoordinates(
+        lookaheadScope: LookaheadScope,
+        onLookaheadPassCoordinates:
+            (
+                lookaheadScopeCoordinates: LayoutCoordinates, layoutCoordinates: LayoutCoordinates
+            ) -> Unit
+    ): Modifier =
+        with(lookaheadScope) {
+            [email protected] { measurable, constraints ->
+                val placeable = measurable.measure(constraints)
+                layout(placeable.width, placeable.height) {
+                    if (isLookingAhead) {
+                        coordinates?.let { coordinates ->
+                            onLookaheadPassCoordinates(lookaheadScopeCoordinates, coordinates)
+                        }
+                    }
+                    placeable.place(0, 0)
+                }
+            }
+        }
+
+    /** Capture LookaheadScope coordinates during the Approach pass. */
+    private fun Modifier.onApproachPassCoordinates(
+        lookaheadScope: LookaheadScope,
+        onApproachPassCoordinates:
+            (
+                lookaheadScopeCoordinates: LayoutCoordinates, layoutCoordinates: LayoutCoordinates
+            ) -> Unit
+    ): Modifier =
+        with(lookaheadScope) {
+            [email protected](
+                isMeasurementApproachInProgress = { false },
+                isPlacementApproachInProgress = {
+                    onApproachPassCoordinates(lookaheadScopeCoordinates, it)
+                    false
+                },
+                approachMeasure = { measurable, constraints ->
+                    val placeable = measurable.measure(constraints)
+                    layout(placeable.width, placeable.height) { placeable.place(0, 0) }
+                }
+            )
+        }
+
     private fun assertSameLayoutWithAndWithoutLookahead(
         content: @Composable (modifier: Modifier) -> Unit
     ) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt
index 9c1faaf..52695a6 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt
@@ -141,20 +141,26 @@
                     )
                 }
         } else {
-            val rootDelegate = lookaheadDelegate.rootLookaheadDelegate
             // This is a case of mixed coordinates where `this` is lookahead coords, and
             // `sourceCoordinates` isn't. Therefore we'll break this into two parts:
             // local position in lookahead coords space && local position in regular layout coords
             // space.
+            val rootDelegate = lookaheadDelegate.rootLookaheadDelegate
+
             val localLookaheadPos =
                 localPositionOf(
                     sourceCoordinates = rootDelegate.lookaheadLayoutCoordinates,
                     relativeToSource = relativeToSource,
                     includeMotionFrameOfReference = includeMotionFrameOfReference
-                )
+                ) - rootDelegate.position.toOffset()
+
+            // If Lookahead is the hierarchy's absolute root (no parent), we may use its coordinates
+            // directly
+            val rootDelegateCoordinates =
+                rootDelegate.coordinator.parentCoordinates ?: rootDelegate.coordinator.coordinates
 
             val localPos =
-                rootDelegate.coordinator.coordinates.localPositionOf(
+                rootDelegateCoordinates.localPositionOf(
                     sourceCoordinates = sourceCoordinates,
                     relativeToSource = Offset.Zero,
                     includeMotionFrameOfReference = includeMotionFrameOfReference
diff --git a/core/core-telecom/src/main/res/values-af/strings.xml b/core/core-telecom/src/main/res/values-af/strings.xml
new file mode 100644
index 0000000..e5c55e0
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-af/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Oorstuk"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kabelkopstuk"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Luidspreker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-am/strings.xml b/core/core-telecom/src/main/res/values-am/strings.xml
new file mode 100644
index 0000000..66301fb
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-am/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ማዳመጫ"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ባለ ገመድ ማዳመጫ"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"ድምፅ ማውጫ"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ar/strings.xml b/core/core-telecom/src/main/res/values-ar/strings.xml
new file mode 100644
index 0000000..8963293
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ar/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"سماعة أذن"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"سماعة رأس سلكية"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"مكبِّر صوت"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-as/strings.xml b/core/core-telecom/src/main/res/values-as/strings.xml
new file mode 100644
index 0000000..697b692
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-as/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ইয়েৰপিচ"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"তাঁৰযুক্ত হেডছেট"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"স্পীকাৰ"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-az/strings.xml b/core/core-telecom/src/main/res/values-az/strings.xml
new file mode 100644
index 0000000..e62a4d0
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-az/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Qulaqlıq"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Simli qulaqlıq"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Dinamik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-b+sr+Latn/strings.xml b/core/core-telecom/src/main/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..6afc996
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Slušalica"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Žičane slušalice"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Zvučnik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-be/strings.xml b/core/core-telecom/src/main/res/values-be/strings.xml
new file mode 100644
index 0000000..cb6122ec
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-be/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Дынамік"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Правадная гарнітура"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Гучная сувязь"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-bn/strings.xml b/core/core-telecom/src/main/res/values-bn/strings.xml
new file mode 100644
index 0000000..5acdea3
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-bn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ইয়ারপিস"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ওয়্যার্ড হেডসেট"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"স্পিকার"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-bs/strings.xml b/core/core-telecom/src/main/res/values-bs/strings.xml
new file mode 100644
index 0000000..6afc996
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-bs/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Slušalica"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Žičane slušalice"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Zvučnik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ca/strings.xml b/core/core-telecom/src/main/res/values-ca/strings.xml
new file mode 100644
index 0000000..4b8b784
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ca/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Auricular"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Auriculars amb cable"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Altaveu"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-cs/strings.xml b/core/core-telecom/src/main/res/values-cs/strings.xml
new file mode 100644
index 0000000..9bafe17
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-cs/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Sluchátko"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kabelová náhlavní souprava"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Reproduktor"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-da/strings.xml b/core/core-telecom/src/main/res/values-da/strings.xml
new file mode 100644
index 0000000..257ec4e
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-da/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Højttaler"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Headset med ledning"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Højttaler"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-de/strings.xml b/core/core-telecom/src/main/res/values-de/strings.xml
new file mode 100644
index 0000000..0ce2c07
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-de/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Kopfhörer"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kabelgebundenes Headset"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Lautsprecher"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-el/strings.xml b/core/core-telecom/src/main/res/values-el/strings.xml
new file mode 100644
index 0000000..83770ab
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-el/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Ακουστικό τηλεφώνου"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Ενσύρματα ακουστικά"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Ηχείο"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-en-rAU/strings.xml b/core/core-telecom/src/main/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..ae0ae39
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-en-rAU/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Earpiece"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Wired headset"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-en-rGB/strings.xml b/core/core-telecom/src/main/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..ae0ae39
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-en-rGB/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Earpiece"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Wired headset"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-en-rIN/strings.xml b/core/core-telecom/src/main/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..ae0ae39
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-en-rIN/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Earpiece"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Wired headset"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-es-rUS/strings.xml b/core/core-telecom/src/main/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..1b87fa9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-es-rUS/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Auricular"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Auriculares con cable"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Bocina"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-es/strings.xml b/core/core-telecom/src/main/res/values-es/strings.xml
new file mode 100644
index 0000000..5af28da
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-es/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Auricular"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Auriculares con cable"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Altavoz"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-et/strings.xml b/core/core-telecom/src/main/res/values-et/strings.xml
new file mode 100644
index 0000000..3d2cc0c
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-et/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Kuular"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Juhtmega peakomplekt"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Kõlar"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-eu/strings.xml b/core/core-telecom/src/main/res/values-eu/strings.xml
new file mode 100644
index 0000000..16cc5e9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-eu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Aurikularra"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Entzungailu kableduna"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Bozgorailua"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-fa/strings.xml b/core/core-telecom/src/main/res/values-fa/strings.xml
new file mode 100644
index 0000000..e159d7c
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-fa/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"گوشی"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"هدست سیمی"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"بلندگو"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-fr-rCA/strings.xml b/core/core-telecom/src/main/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..33d0ca4
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-fr-rCA/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Écouteur"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Écouteurs filaires"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Haut-parleur"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-fr/strings.xml b/core/core-telecom/src/main/res/values-fr/strings.xml
new file mode 100644
index 0000000..efba6ec
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-fr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Écouteur"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Casque filaire"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Haut-parleur"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-gl/strings.xml b/core/core-telecom/src/main/res/values-gl/strings.xml
new file mode 100644
index 0000000..73f3cf5
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-gl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Auricular"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Auriculares con cable"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Altofalante"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-gu/strings.xml b/core/core-telecom/src/main/res/values-gu/strings.xml
new file mode 100644
index 0000000..eb7a9ee
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-gu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ઇયરપીસ"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"વાયર્ડ હૅડસેટ"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"સ્પીકર"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-hr/strings.xml b/core/core-telecom/src/main/res/values-hr/strings.xml
new file mode 100644
index 0000000..a812b93
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-hr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Zvučnik"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Žičane slušalice"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Zvučnik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-hu/strings.xml b/core/core-telecom/src/main/res/values-hu/strings.xml
new file mode 100644
index 0000000..1175ec2
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-hu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Fülhallgató"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Vezetékes headset"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Hangszóró"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-hy/strings.xml b/core/core-telecom/src/main/res/values-hy/strings.xml
new file mode 100644
index 0000000..0f1a0d2
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-hy/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Լսափող"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Լարով ականջակալ"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Բարձրախոս"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-in/strings.xml b/core/core-telecom/src/main/res/values-in/strings.xml
new file mode 100644
index 0000000..8e053cc
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-in/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Earpiece"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Headset berkabel"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-is/strings.xml b/core/core-telecom/src/main/res/values-is/strings.xml
new file mode 100644
index 0000000..c0befd0
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-is/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Hátalari"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Höfuðtól með snúru"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Hátalari"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-iw/strings.xml b/core/core-telecom/src/main/res/values-iw/strings.xml
new file mode 100644
index 0000000..9ec7ee9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-iw/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"אוזניה"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"אוזניות חוטיות"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"רמקול"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-kk/strings.xml b/core/core-telecom/src/main/res/values-kk/strings.xml
new file mode 100644
index 0000000..6867ece
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-kk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Телефон динамигі"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Сымды гарнитура"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Динамик"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-km/strings.xml b/core/core-telecom/src/main/res/values-km/strings.xml
new file mode 100644
index 0000000..0fc75bb
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-km/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ឧបករណ៍ស្ដាប់សំឡេង"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"កាស​មាន​ខ្សែ"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"ឧបករណ៍​បំពង​សំឡេង"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-kn/strings.xml b/core/core-telecom/src/main/res/values-kn/strings.xml
new file mode 100644
index 0000000..4b006c3
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-kn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ಇಯರ್‌ಪೀಸ್‌"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ವೈಯರ್ಡ್ ಹೆಡ್‌ಸೆಟ್‌"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"ಸ್ಪೀಕರ್"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ko/strings.xml b/core/core-telecom/src/main/res/values-ko/strings.xml
new file mode 100644
index 0000000..6dd6a7d
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ko/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"스피커"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"유선 헤드셋"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"스피커"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ky/strings.xml b/core/core-telecom/src/main/res/values-ky/strings.xml
new file mode 100644
index 0000000..4de5b55
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ky/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Кулакчын"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Зымдуу гарнитура"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Динамик"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-lt/strings.xml b/core/core-telecom/src/main/res/values-lt/strings.xml
new file mode 100644
index 0000000..ab5b490
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-lt/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Garsiakalbis prie ausies"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Laidinės ausinės"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Garsiakalbis"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-lv/strings.xml b/core/core-telecom/src/main/res/values-lv/strings.xml
new file mode 100644
index 0000000..dbbe825
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-lv/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Auss skaļrunis"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Vadu austiņas"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Skaļrunis"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-mk/strings.xml b/core/core-telecom/src/main/res/values-mk/strings.xml
new file mode 100644
index 0000000..73a43de
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-mk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Слушалка"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Жичени слушалки"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Звучник"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-mn/strings.xml b/core/core-telecom/src/main/res/values-mn/strings.xml
new file mode 100644
index 0000000..3f929e9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-mn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Чихний спикер"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Утастай чихэвч"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Чанга яригч"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-mr/strings.xml b/core/core-telecom/src/main/res/values-mr/strings.xml
new file mode 100644
index 0000000..343c6c2
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-mr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"इअरपीस"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"वायर्ड हेडसेट"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"स्पीकर"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ms/strings.xml b/core/core-telecom/src/main/res/values-ms/strings.xml
new file mode 100644
index 0000000..1ffead0
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ms/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Alat dengar"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Set kepala berwayar"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Pembesar suara"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-my/strings.xml b/core/core-telecom/src/main/res/values-my/strings.xml
new file mode 100644
index 0000000..a62aea0
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-my/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"တယ်လီဖုန်းနားခွက်"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ကြိုးတပ် မိုက်ခွက်ပါနားကြပ်"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"စပီကာ"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-nb/strings.xml b/core/core-telecom/src/main/res/values-nb/strings.xml
new file mode 100644
index 0000000..387722e
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-nb/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Ørehøyttaler"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Hodetelefoner med ledning"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Høyttaler"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-nl/strings.xml b/core/core-telecom/src/main/res/values-nl/strings.xml
new file mode 100644
index 0000000..c496439
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-nl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Oortelefoon"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Bedrade headset"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-or/strings.xml b/core/core-telecom/src/main/res/values-or/strings.xml
new file mode 100644
index 0000000..2a480c7
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-or/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ଇୟରପିସ"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ତାରଯୁକ୍ତ ହେଡସେଟ"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"ସ୍ପିକର"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-pa/strings.xml b/core/core-telecom/src/main/res/values-pa/strings.xml
new file mode 100644
index 0000000..ebc4300
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-pa/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ਈਅਰਪੀਸ"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ਤਾਰ ਵਾਲਾ ਹੈੱਡਸੈੱਟ"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"ਸਪੀਕਰ"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-pl/strings.xml b/core/core-telecom/src/main/res/values-pl/strings.xml
new file mode 100644
index 0000000..768b794
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-pl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Słuchawka"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Przewodowy zestaw słuchawkowy"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Głośnik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-pt-rBR/strings.xml b/core/core-telecom/src/main/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..de52295
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-pt-rBR/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Minifone de ouvido"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Fone de ouvido com fio"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Alto-falante"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-pt/strings.xml b/core/core-telecom/src/main/res/values-pt/strings.xml
new file mode 100644
index 0000000..de52295
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-pt/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Minifone de ouvido"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Fone de ouvido com fio"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Alto-falante"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ro/strings.xml b/core/core-telecom/src/main/res/values-ro/strings.xml
new file mode 100644
index 0000000..a1b63da
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ro/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Cască"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Set de căști-microfon cu fir"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Difuzor"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ru/strings.xml b/core/core-telecom/src/main/res/values-ru/strings.xml
new file mode 100644
index 0000000..9f1c287
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ru/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Наушник"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Проводная гарнитура"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Колонка"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-si/strings.xml b/core/core-telecom/src/main/res/values-si/strings.xml
new file mode 100644
index 0000000..748d5c2
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-si/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"සවන් කඩ"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"රැහැන්ගත කළ හෙඩ්සෙට්"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"ස්පීකරය"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sk/strings.xml b/core/core-telecom/src/main/res/values-sk/strings.xml
new file mode 100644
index 0000000..b6075f4
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Slúchadlo"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Káblové slúchadlá s mikrofónom"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Reproduktor"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sl/strings.xml b/core/core-telecom/src/main/res/values-sl/strings.xml
new file mode 100644
index 0000000..5ca1886
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Slušalka"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Žične slušalke z mikrofonom"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Zvočnik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sq/strings.xml b/core/core-telecom/src/main/res/values-sq/strings.xml
new file mode 100644
index 0000000..c932e8d
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sq/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Receptor"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kufje me tel"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Altoparlant"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sr/strings.xml b/core/core-telecom/src/main/res/values-sr/strings.xml
new file mode 100644
index 0000000..199a682
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Слушалица"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Жичане слушалице"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Звучник"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sv/strings.xml b/core/core-telecom/src/main/res/values-sv/strings.xml
new file mode 100644
index 0000000..aec1523
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sv/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Lur"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kabelanslutet headset"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Högtalare"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sw/strings.xml b/core/core-telecom/src/main/res/values-sw/strings.xml
new file mode 100644
index 0000000..af27136d
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sw/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Spika ya sikioni"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Vifaa vya sauti vyenye waya"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Spika"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ta/strings.xml b/core/core-telecom/src/main/res/values-ta/strings.xml
new file mode 100644
index 0000000..b3678bb
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ta/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ஒலி கேட்கும் பகுதி"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"வயர் ஹெட்செட்"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"ஸ்பீக்கர்"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-te/strings.xml b/core/core-telecom/src/main/res/values-te/strings.xml
new file mode 100644
index 0000000..e387597
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-te/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ఇయర్‌పీస్"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"వైర్ ఉన్న హెడ్‌సెట్"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"స్పీకర్"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-th/strings.xml b/core/core-telecom/src/main/res/values-th/strings.xml
new file mode 100644
index 0000000..103ceb1
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-th/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"หูฟังโทรศัพท์"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ชุดหูฟังแบบมีสาย"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"ลำโพง"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-tl/strings.xml b/core/core-telecom/src/main/res/values-tl/strings.xml
new file mode 100644
index 0000000..50ac56c
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-tl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Earpiece"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Wired na headset"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-tr/strings.xml b/core/core-telecom/src/main/res/values-tr/strings.xml
new file mode 100644
index 0000000..600c6d2
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-tr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Kulaklık"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kablolu mikrofonlu kulaklık"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Hoparlör"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-uk/strings.xml b/core/core-telecom/src/main/res/values-uk/strings.xml
new file mode 100644
index 0000000..d8580f9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-uk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Динамік"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Дротова гарнітура"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Колонка"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ur/strings.xml b/core/core-telecom/src/main/res/values-ur/strings.xml
new file mode 100644
index 0000000..d577f0b
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ur/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ایئر پیس"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"تار والا ہیڈ سیٹ"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"اسپیکر"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-uz/strings.xml b/core/core-telecom/src/main/res/values-uz/strings.xml
new file mode 100644
index 0000000..679f5dd
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-uz/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Quloq karnaychasi"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Simli garnitura"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Karnay"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-vi/strings.xml b/core/core-telecom/src/main/res/values-vi/strings.xml
new file mode 100644
index 0000000..a4c82f9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-vi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Loa tai nghe"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Tai nghe có dây"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Loa ngoài"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-zh-rCN/strings.xml b/core/core-telecom/src/main/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..0d4efb7
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-zh-rCN/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"手机听筒"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"有线头戴式耳机"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"扬声器"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-zh-rHK/strings.xml b/core/core-telecom/src/main/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..4e99591
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-zh-rHK/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"聽筒"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"有線耳機"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"喇叭"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-zh-rTW/strings.xml b/core/core-telecom/src/main/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..4c586c7
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-zh-rTW/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"耳機"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"有線耳機"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"喇叭"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-zu/strings.xml b/core/core-telecom/src/main/res/values-zu/strings.xml
new file mode 100644
index 0000000..7aab3c5
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-zu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Isipikha sendlebe"</string>
+    <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Iheadset enentambo"</string>
+    <string name="callendpoint_name_speaker" msgid="623806810712383295">"Isipikha"</string>
+</resources>
diff --git a/core/core/build.gradle b/core/core/build.gradle
index 92490a9..bae3ce7 100644
--- a/core/core/build.gradle
+++ b/core/core/build.gradle
@@ -106,4 +106,5 @@
     description = "Provides backward-compatible implementations of Android platform APIs and " +
             "features."
     failOnDeprecationWarnings = false
+    samples(project(":core:core:core-samples"))
 }
diff --git a/core/core/integration-tests/publishing/src/test/kotlin/androidx/build/ConstraintTest.kt b/core/core/integration-tests/publishing/src/test/kotlin/androidx/build/ConstraintTest.kt
index 545a3d6..c96cd6d 100644
--- a/core/core/integration-tests/publishing/src/test/kotlin/androidx/build/ConstraintTest.kt
+++ b/core/core/integration-tests/publishing/src/test/kotlin/androidx/build/ConstraintTest.kt
@@ -63,15 +63,32 @@
               }
             }
           ],
-          "dependencyConstraints": [
+          variants: [
             {
-              "group": "androidx.preference",
-              "module": "preference-ktx",
-              "version": {
-                "requires": "1.3.0-alpha01"
-              }
-            }
-          ],
+              "name": "releaseVariantReleaseApiPublication",
+              "dependencyConstraints": [
+                {
+                   "group": "org.jetbrains.kotlin",
+                   "module": "kotlin-stdlib",
+                   "version": {
+                     "requires": "1.8.22"
+                   }
+                }
+              ],
+             },
+             {
+              "name": "releaseVariantReleaseRuntimePublication",
+              "dependencyConstraints": [
+                {
+                  "group": "androidx.preference",
+                  "module": "preference-ktx",
+                  "version": {
+                    "requires": "1.3.0-alpha01"
+                  }
+                }
+              ],
+             }
+          ]
           "files": [
             {
               "name": "preference-1.3.0-alpha01.aar",
@@ -85,7 +102,7 @@
     }
 
     private fun getConstraintVersion(metadata: String, groupId: String, artifact: String): String? =
-        getDependencyConstraints(metadata)?.let {
+        getDependencyConstraints(metadata).let {
             Regex(
                     "\"group\": \"$groupId\",\\s+\"module\": " +
                         "\"$artifact\",\\s+\"version\": \\{\\s+\"requires\": \"(.+?)\""
@@ -97,8 +114,8 @@
         }
 
     private fun getDependencyConstraints(moduleJson: String) =
-        moduleJson.let {
-            Regex("(?s)\"dependencyConstraints\": \\[(.+?)]").find(it)?.groups?.get(1)?.value
+        Regex("(?s)\"dependencyConstraints\": \\[(.+?)]").findAll(moduleJson).joinToString("\n") {
+            it.groups.get(1)?.value.orEmpty()
         }
 
     // Yes, I know https://stackoverflow.com/a/1732454/258688, but it's just a test...
diff --git a/core/core/samples/build.gradle b/core/core/samples/build.gradle
new file mode 100644
index 0000000..0d008cd
--- /dev/null
+++ b/core/core/samples/build.gradle
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+import androidx.build.LibraryType
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+dependencies {
+    compileOnly(project(":annotation:annotation-sampled"))
+    implementation(project(":core:core"))
+}
+android {
+    compileSdk 35
+    namespace "androidx.core.samples"
+}
+androidx {
+    name = "AndroidX Core Samples"
+    type = LibraryType.SAMPLES
+    mavenVersion = LibraryVersions.CORE
+    inceptionYear = "2024"
+    description = "Samples for the AndroidX Core Libraries"
+}
diff --git a/core/core/samples/src/main/java/androidx/core/os/ProfilingSamples.kt b/core/core/samples/src/main/java/androidx/core/os/ProfilingSamples.kt
new file mode 100644
index 0000000..716675b
--- /dev/null
+++ b/core/core/samples/src/main/java/androidx/core/os/ProfilingSamples.kt
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2024 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.core.os
+
+import android.content.Context
+import android.os.CancellationSignal
+import android.os.ProfilingResult
+import androidx.annotation.Sampled
+import java.util.function.Consumer
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.flow.flowOn
+
+/** Sample showing how to request a java heap dump with various optional parameters. */
+@Sampled
+fun requestJavaHeapDump(context: Context) {
+    val listener =
+        Consumer<ProfilingResult> { profilingResult ->
+            if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
+                doSomethingWithMyFile(profilingResult.resultFilePath)
+            } else {
+                doSomethingWithFailure(profilingResult.errorCode, profilingResult.errorMessage)
+            }
+        }
+
+    requestProfiling(
+        context,
+        JavaHeapDumpRequestBuilder()
+            .setBufferSizeKb(123 /* Requested buffer size in KB */)
+            .setTag("tag" /* Caller supplied tag for identification */)
+            .build(),
+        Dispatchers.IO.asExecutor(), // Your choice of executor for the callback to occur on.
+        listener
+    )
+}
+
+/**
+ * Sample showing how to request a heap profile with various optional parameters and optional
+ * cancellation after the event of interest was captured.
+ */
+@Sampled
+fun requestHeapProfile(context: Context) {
+    val listener =
+        Consumer<ProfilingResult> { profilingResult ->
+            if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
+                doSomethingWithMyFile(profilingResult.resultFilePath)
+            } else {
+                doSomethingWithFailure(profilingResult.errorCode, profilingResult.errorMessage)
+            }
+        }
+
+    val cancellationSignal = CancellationSignal()
+
+    requestProfiling(
+        context,
+        HeapProfileRequestBuilder()
+            .setBufferSizeKb(1000 /* Requested buffer size in KB */)
+            .setDurationMs(5 * 1000 /* Requested profiling duration in milliseconds */)
+            .setTrackJavaAllocations(true)
+            .setSamplingIntervalBytes(100 /* Requested sampling interval in bytes */)
+            .setTag("tag" /* Caller supplied tag for identification */)
+            .setCancellationSignal(cancellationSignal)
+            .build(),
+        Dispatchers.IO.asExecutor(), // Your choice of executor for the callback to occur on.
+        listener
+    )
+
+    // Optionally, wait for something interesting to happen and then stop the profiling to receive
+    // the result as is.
+    cancellationSignal.cancel()
+}
+
+/**
+ * Sample showing how to request a stack sample with various optional parameters and optional
+ * cancellation after the event of interest was captured.
+ */
+@Sampled
+fun requestStackSampling(context: Context) {
+    val listener =
+        Consumer<ProfilingResult> { profilingResult ->
+            if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
+                doSomethingWithMyFile(profilingResult.resultFilePath)
+            } else {
+                doSomethingWithFailure(profilingResult.errorCode, profilingResult.errorMessage)
+            }
+        }
+
+    val cancellationSignal = CancellationSignal()
+
+    requestProfiling(
+        context,
+        StackSamplingRequestBuilder()
+            .setBufferSizeKb(1000 /* Requested buffer size in KB */)
+            .setDurationMs(10 * 1000 /* Requested profiling duration in millisconds */)
+            .setSamplingFrequencyHz(100 /* Requested sampling frequency */)
+            .setTag("tag" /* Caller supplied tag for identification */)
+            .setCancellationSignal(cancellationSignal)
+            .build(),
+        Dispatchers.IO.asExecutor(), // Your choice of executor for the callback to occur on.
+        listener
+    )
+
+    // Optionally, wait for something interesting to happen and then stop the profiling to receive
+    // the result as is.
+    cancellationSignal.cancel()
+}
+
+/**
+ * Sample showing how to request a system trace with various optional parameters and optional
+ * cancellation after the event of interest was captured.
+ */
+@Sampled
+fun requestSystemTrace(context: Context) {
+    val listener =
+        Consumer<ProfilingResult> { profilingResult ->
+            if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
+                doSomethingWithMyFile(profilingResult.resultFilePath)
+            } else {
+                doSomethingWithFailure(profilingResult.errorCode, profilingResult.errorMessage)
+            }
+        }
+
+    val cancellationSignal = CancellationSignal()
+
+    requestProfiling(
+        context,
+        SystemTraceRequestBuilder()
+            .setBufferSizeKb(1000 /* Requested buffer size in KB */)
+            .setDurationMs(60 * 1000 /* Requested profiling duration in millisconds */)
+            .setBufferFillPolicy(BufferFillPolicy.RING_BUFFER /* Buffer fill policy */)
+            .setTag("tag" /* Caller supplied tag for identification */)
+            .setCancellationSignal(cancellationSignal)
+            .build(),
+        Dispatchers.IO.asExecutor(), // Your choice of executor for the callback to occur on.
+        listener
+    )
+
+    // Optionally, wait for something interesting to happen and then stop the profiling to receive
+    // the result as is.
+    cancellationSignal.cancel()
+}
+
+/** Sample showing how to register a listener for all profiling results from your app. */
+@Sampled
+fun registerForAllProfilingResultsSample(context: Context) {
+    val listener =
+        Consumer<ProfilingResult> { profilingResult ->
+            if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
+                doSomethingWithMyFile(profilingResult.resultFilePath)
+            } else {
+                doSomethingWithFailure(profilingResult.errorCode, profilingResult.errorMessage)
+            }
+        }
+
+    registerForAllProfilingResults(
+        context,
+        Dispatchers.IO.asExecutor(), // Your choice of executor for the callback to occur on.
+        listener
+    )
+}
+
+/** Sample showing how to register a flow for all profiling results from your app. */
+@Sampled
+suspend fun registerForAllProfilingResultsFlowSample(context: Context) {
+    val flow = registerForAllProfilingResults(context)
+
+    flow
+        .flowOn(Dispatchers.IO) // Consume files on a background thread
+        .collect { profilingResult ->
+            if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
+                doSomethingWithMyFile(profilingResult.resultFilePath)
+            } else {
+                doSomethingWithFailure(profilingResult.errorCode, profilingResult.errorMessage)
+            }
+        }
+}
+
+@Suppress("UNUSED_PARAMETER") fun doSomethingWithMyFile(filePath: String?) {}
+
+@Suppress("UNUSED_PARAMETER") fun doSomethingWithFailure(errorCode: Int, errorMessage: String?) {}
diff --git a/core/core/src/main/java/androidx/core/os/Profiling.kt b/core/core/src/main/java/androidx/core/os/Profiling.kt
index b73acb1..683e0fbb 100644
--- a/core/core/src/main/java/androidx/core/os/Profiling.kt
+++ b/core/core/src/main/java/androidx/core/os/Profiling.kt
@@ -64,7 +64,11 @@
     private class RingBuffer : BufferFillPolicy(VALUE_BUFFER_FILL_POLICY_RING_BUFFER)
 }
 
-/** Obtain a flow to be called with all profiling results for this UID. */
+/**
+ * Obtain a flow to be called with all profiling results for this UID.
+ *
+ * @sample androidx.core.os.registerForAllProfilingResultsFlowSample
+ */
 @RequiresApi(api = 35)
 public fun registerForAllProfilingResults(context: Context): Flow<ProfilingResult> = callbackFlow {
     val listener = Consumer<ProfilingResult> { result -> trySend(result) }
@@ -75,7 +79,11 @@
     awaitClose { service.unregisterForAllProfilingResults(listener) }
 }
 
-/** Register a listener to be called with all profiling results for this UID. */
+/**
+ * Register a listener to be called with all profiling results for this UID.
+ *
+ * @sample androidx.core.os.registerForAllProfilingResultsSample
+ */
 @RequiresApi(api = 35)
 public fun registerForAllProfilingResults(
     context: Context,
@@ -98,6 +106,19 @@
  *
  * If the executor and/or listener are null, and if no global listener and executor combinations are
  * registered using [registerForAllProfilingResults], the request will be dropped.
+ *
+ * Requests will be rate limited and are not guaranteed to be filled.
+ *
+ * There might be a delay before profiling begins. For continuous profiling types (system tracing,
+ * stack sampling, and heap profiling), we recommend starting the collection early and stopping it
+ * with cancellationSignal, set on the [profilingRequest] builder, immediately after the area of
+ * interest to ensure that the section you want profiled is captured. For heap dumps, we recommend
+ * testing locally to ensure that the heap dump is collected at the proper time.
+ *
+ * @sample androidx.core.os.requestJavaHeapDump
+ * @sample androidx.core.os.requestHeapProfile
+ * @sample androidx.core.os.requestStackSampling
+ * @sample androidx.core.os.requestSystemTrace
  */
 @RequiresApi(api = 35)
 public fun requestProfiling(
@@ -164,7 +185,11 @@
     protected abstract fun getParams(): Bundle
 }
 
-/** Request builder to create a request for a java heap dump from [ProfilingManager]. */
+/**
+ * Request builder to create a request for a java heap dump from [ProfilingManager].
+ *
+ * @sample androidx.core.os.requestJavaHeapDump
+ */
 @RequiresApi(api = 35)
 public class JavaHeapDumpRequestBuilder : ProfilingRequestBuilder<JavaHeapDumpRequestBuilder>() {
     private val mParams: Bundle = Bundle()
@@ -191,7 +216,11 @@
     }
 }
 
-/** Request builder to create a request for a heap profile from [ProfilingManager]. */
+/**
+ * Request builder to create a request for a heap profile from [ProfilingManager].
+ *
+ * @sample androidx.core.os.requestHeapProfile
+ */
 @RequiresApi(api = 35)
 public class HeapProfileRequestBuilder : ProfilingRequestBuilder<HeapProfileRequestBuilder>() {
     private val mParams: Bundle = Bundle()
@@ -236,7 +265,11 @@
     }
 }
 
-/** Request builder to create a request for stack sampling from [ProfilingManager]. */
+/**
+ * Request builder to create a request for stack sampling from [ProfilingManager].
+ *
+ * @sample androidx.core.os.requestStackSampling
+ */
 @RequiresApi(api = 35)
 public class StackSamplingRequestBuilder : ProfilingRequestBuilder<StackSamplingRequestBuilder>() {
     private val mParams: Bundle = Bundle()
@@ -275,7 +308,11 @@
     }
 }
 
-/** Request builder to create a request for a system trace from [ProfilingManager]. */
+/**
+ * Request builder to create a request for a system trace from [ProfilingManager].
+ *
+ * @sample androidx.core.os.requestSystemTrace
+ */
 @RequiresApi(api = 35)
 public class SystemTraceRequestBuilder : ProfilingRequestBuilder<SystemTraceRequestBuilder>() {
     private val mParams: Bundle = Bundle()
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BiometricPromptDataJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BiometricPromptDataJavaTest.java
index 4cb0d808..0a2d095 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BiometricPromptDataJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BiometricPromptDataJavaTest.java
@@ -41,7 +41,7 @@
 public class BiometricPromptDataJavaTest {
 
     private static final BiometricPrompt.CryptoObject TEST_CRYPTO_OBJECT = BiometricTestUtils
-            .INSTANCE.createCryptoObject$credentials_debugAndroidTest();
+            .INSTANCE.createCryptoObject$credentials_releaseAndroidTest();
 
     private static final long DEFAULT_BUNDLE_LONG_FOR_CRYPTO_ID = 0L;
 
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerApi34JavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerApi34JavaTest.java
index f6cf56d..031750f 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerApi34JavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerApi34JavaTest.java
@@ -73,7 +73,7 @@
     public void test_retrieveProviderCreateCredReqWithSuccessBpAuthJetpack_retrieveJetpackResult() {
         for (int jetpackResult :
                 AuthenticationResult.Companion
-                        .getBiometricFrameworkToJetpackResultMap$credentials_debug().values()) {
+                        .getBiometricFrameworkToJetpackResultMap$credentials_release().values()) {
             BiometricPromptResult biometricPromptResult =
                     new BiometricPromptResult(new AuthenticationResult(jetpackResult));
             android.service.credentials.CreateCredentialRequest request =
@@ -97,7 +97,7 @@
     public void test_retrieveProviderGetCredReqWithSuccessBpAuthJetpack_retrieveJetpackResult() {
         for (int jetpackResult :
                 AuthenticationResult.Companion
-                        .getBiometricFrameworkToJetpackResultMap$credentials_debug().values()) {
+                        .getBiometricFrameworkToJetpackResultMap$credentials_release().values()) {
             BiometricPromptResult biometricPromptResult =
                     new BiometricPromptResult(new AuthenticationResult(jetpackResult));
             Intent intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST,
@@ -118,10 +118,10 @@
     public void test_retrieveProviderCreateCredReqWithSuccessBpAuthFramework_resultConverted() {
         for (int frameworkResult :
                 AuthenticationResult.Companion
-                        .getBiometricFrameworkToJetpackResultMap$credentials_debug().keySet()) {
+                        .getBiometricFrameworkToJetpackResultMap$credentials_release().keySet()) {
             BiometricPromptResult biometricPromptResult =
                     new BiometricPromptResult(
-                            AuthenticationResult.Companion.createFrom$credentials_debug(
+                            AuthenticationResult.Companion.createFrom$credentials_release(
                                     frameworkResult,
                                     /*isFrameworkBiometricPrompt=*/true
                             ));
@@ -129,7 +129,7 @@
                     TestUtilsKt.setUpCreatePasswordRequest();
             int expectedResult =
                     AuthenticationResult.Companion
-                            .getBiometricFrameworkToJetpackResultMap$credentials_debug()
+                            .getBiometricFrameworkToJetpackResultMap$credentials_release()
                             .get(frameworkResult);
             Intent intent = prepareIntentWithCreateRequest(
                     request,
@@ -150,16 +150,16 @@
     public void test_retrieveProviderGetCredReqWithSuccessBpAuthFramework_resultConverted() {
         for (int frameworkResult :
                 AuthenticationResult.Companion
-                        .getBiometricFrameworkToJetpackResultMap$credentials_debug().keySet()) {
+                        .getBiometricFrameworkToJetpackResultMap$credentials_release().keySet()) {
             BiometricPromptResult biometricPromptResult =
                     new BiometricPromptResult(
-                            AuthenticationResult.Companion.createFrom$credentials_debug(
+                            AuthenticationResult.Companion.createFrom$credentials_release(
                                     frameworkResult,
                                     /*isFrameworkBiometricPrompt=*/true
                             ));
             int expectedResult =
                     AuthenticationResult.Companion
-                            .getBiometricFrameworkToJetpackResultMap$credentials_debug()
+                            .getBiometricFrameworkToJetpackResultMap$credentials_release()
                             .get(frameworkResult);
             Intent intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST,
                     biometricPromptResult);
@@ -180,7 +180,7 @@
     public void test_retrieveProviderCreateCredReqWithFailureBpAuthJetpack_retrieveJetpackError() {
         for (int jetpackError :
                 AuthenticationError.Companion
-                        .getBiometricFrameworkToJetpackErrorMap$credentials_debug().values()) {
+                        .getBiometricFrameworkToJetpackErrorMap$credentials_release().values()) {
             BiometricPromptResult biometricPromptResult =
                     new BiometricPromptResult(
                             new AuthenticationError(
@@ -207,7 +207,7 @@
     public void test_retrieveProviderGetCredReqWithFailureBpAuthJetpack_retrieveJetpackError() {
         for (int jetpackError :
                 AuthenticationError.Companion
-                        .getBiometricFrameworkToJetpackErrorMap$credentials_debug().values()) {
+                        .getBiometricFrameworkToJetpackErrorMap$credentials_release().values()) {
             BiometricPromptResult biometricPromptResult = new BiometricPromptResult(
                     new AuthenticationError(
                             jetpackError,
@@ -232,10 +232,10 @@
     public void test_retrieveProviderCreateCredReqWithFailureBpAuthFramework_errorConverted() {
         for (int frameworkError :
                 AuthenticationError.Companion
-                        .getBiometricFrameworkToJetpackErrorMap$credentials_debug().keySet()) {
+                        .getBiometricFrameworkToJetpackErrorMap$credentials_release().keySet()) {
             BiometricPromptResult biometricPromptResult =
                     new BiometricPromptResult(
-                            AuthenticationError.Companion.createFrom$credentials_debug(
+                            AuthenticationError.Companion.createFrom$credentials_release(
                                     frameworkError, BIOMETRIC_AUTHENTICATOR_ERROR_MSG,
                                     /*isFrameworkBiometricPrompt=*/true
                             ));
@@ -243,7 +243,7 @@
                     TestUtilsKt.setUpCreatePasswordRequest();
             int expectedErrorCode =
                     AuthenticationError.Companion
-                            .getBiometricFrameworkToJetpackErrorMap$credentials_debug()
+                            .getBiometricFrameworkToJetpackErrorMap$credentials_release()
                             .get(frameworkError);
             Intent intent = prepareIntentWithCreateRequest(
                     request, biometricPromptResult);
@@ -264,9 +264,9 @@
     public void test_retrieveProviderGetCredReqWithFailureBpAuthFramework_correctlyConvertedErr() {
         for (int frameworkError :
                 AuthenticationError.Companion
-                        .getBiometricFrameworkToJetpackErrorMap$credentials_debug().keySet()) {
+                        .getBiometricFrameworkToJetpackErrorMap$credentials_release().keySet()) {
             BiometricPromptResult biometricPromptResult = new BiometricPromptResult(
-                    AuthenticationError.Companion.createFrom$credentials_debug(
+                    AuthenticationError.Companion.createFrom$credentials_release(
                             frameworkError, BIOMETRIC_AUTHENTICATOR_ERROR_MSG,
                             /*isFrameworkBiometricPrompt=*/true
                     ));
@@ -274,7 +274,7 @@
                     biometricPromptResult);
             int expectedErrorCode =
                     AuthenticationError.Companion
-                            .getBiometricFrameworkToJetpackErrorMap$credentials_debug()
+                            .getBiometricFrameworkToJetpackErrorMap$credentials_release()
                             .get(frameworkError);
 
             ProviderGetCredentialRequest retrievedRequest = PendingIntentHandler
diff --git a/datastore/datastore-core/build.gradle b/datastore/datastore-core/build.gradle
index f184764..d509e6a 100644
--- a/datastore/datastore-core/build.gradle
+++ b/datastore/datastore-core/build.gradle
@@ -28,8 +28,6 @@
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
-    id("com.google.protobuf")
-    id ("kotlin-parcelize")
 }
 
 android {
@@ -42,26 +40,6 @@
     namespace "androidx.datastore.core"
 }
 
-protobuf {
-    protoc {
-        artifact = libs.protobufCompiler.get()
-    }
-    generateProtoTasks {
-        all().each { task ->
-            task.builtins {
-                java {
-                    option "lite"
-                }
-            }
-        }
-    }
-}
-
-def protoDir = project.layout.projectDirectory.dir("src/androidInstrumentedTest/proto")
-tasks.named("extractAndroidTestProto").configure {
-    it.inputFiles.from(project.files(protoDir))
-}
-
 androidXMultiplatform {
     jvm()
     mac()
@@ -137,7 +115,6 @@
         androidInstrumentedTest {
             dependsOn(commonJvmTest)
             dependencies {
-                implementation(libs.protobufLite)
                 implementation(libs.truth)
                 implementation(project(":internal-testutils-truth"))
                 implementation(libs.testRunner)
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/AndroidManifest.xml b/datastore/datastore-core/src/androidInstrumentedTest/AndroidManifest.xml
index c25da00..4ec52f2f 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/AndroidManifest.xml
+++ b/datastore/datastore-core/src/androidInstrumentedTest/AndroidManifest.xml
@@ -15,16 +15,5 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
-    <application>
-        <service android:name="androidx.datastore.core.twoWayIpc.TwoWayIpcService"
-            android:enabled="true"
-            android:exported="false"
-            android:process=":TwoWayIpcService" />
-        <service android:name="androidx.datastore.core.twoWayIpc.TwoWayIpcService2"
-            android:enabled="true"
-            android:exported="false"
-            android:process=":TwoWayIpcService2" />
-    </application>
-
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 </manifest>
diff --git a/datastore/integration-tests/testapp/build.gradle b/datastore/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..8266634
--- /dev/null
+++ b/datastore/integration-tests/testapp/build.gradle
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2024 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.
+ */
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+    id("com.google.protobuf")
+    id ("kotlin-parcelize")
+}
+
+android {
+    namespace "androidx.datastore.testapp"
+}
+
+protobuf {
+    protoc {
+        artifact = libs.protobufCompiler.get()
+    }
+    generateProtoTasks {
+        all().each { task ->
+            task.builtins {
+                java {
+                    option "lite"
+                }
+            }
+        }
+    }
+}
+
+dependencies {
+    implementation(libs.okio)
+    implementation(libs.protobufLite)
+    implementation("androidx.lifecycle:lifecycle-service:2.6.1")
+    implementation(project(":datastore:datastore-core"))
+    implementation(project(":datastore:datastore-core-okio"))
+
+    androidTestImplementation(libs.kotlinCoroutinesTest)
+    androidTestImplementation(libs.kotlinTest)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(project(":internal-testutils-truth"))
+    androidTestImplementation(project(":internal-testutils-datastore"))
+    androidTestImplementation(project(":kruth:kruth"))
+}
diff --git a/datastore/integration-tests/testapp/src/androidTest/AndroidManifest.xml b/datastore/integration-tests/testapp/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..867fda8
--- /dev/null
+++ b/datastore/integration-tests/testapp/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2024 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <application>
+        <service android:name="androidx.datastore.testapp.twoWayIpc.TwoWayIpcService"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":TwoWayIpcService" />
+        <service android:name="androidx.datastore.testapp.twoWayIpc.TwoWayIpcService2"
+            android:enabled="true"
+            android:exported="false"
+            android:process=":TwoWayIpcService2" />
+    </application>
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+</manifest>
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/InterProcessCompletableTest.kt b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/InterProcessCompletableTest.kt
similarity index 89%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/InterProcessCompletableTest.kt
rename to datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/InterProcessCompletableTest.kt
index 9623f58..c6233a5 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/InterProcessCompletableTest.kt
+++ b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/InterProcessCompletableTest.kt
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.multiprocess
+package androidx.datastore.testapp.multiprocess
 
 import android.os.Parcelable
-import androidx.datastore.core.twoWayIpc.InterProcessCompletable
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.IpcUnit
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.twoWayIpc.InterProcessCompletable
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.IpcUnit
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.async
 import kotlinx.coroutines.yield
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessDataStoreIpcTest.kt b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessDataStoreIpcTest.kt
similarity index 96%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessDataStoreIpcTest.kt
rename to datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessDataStoreIpcTest.kt
index 86887fc..b13ee2d 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessDataStoreIpcTest.kt
+++ b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessDataStoreIpcTest.kt
@@ -14,21 +14,24 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.multiprocess
+// Parcelize object is testing internal implementation of datastore-core library
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "CANNOT_OVERRIDE_INVISIBLE_MEMBER")
+
+package androidx.datastore.testapp.multiprocess
 
 import androidx.datastore.core.CorruptionException
 import androidx.datastore.core.CorruptionHandler
 import androidx.datastore.core.IOException
 import androidx.datastore.core.SharedCounter
-import androidx.datastore.core.multiprocess.ipcActions.ReadTextAction
-import androidx.datastore.core.multiprocess.ipcActions.SetTextAction
-import androidx.datastore.core.multiprocess.ipcActions.StorageVariant
-import androidx.datastore.core.multiprocess.ipcActions.createMultiProcessTestDatastore
-import androidx.datastore.core.multiprocess.ipcActions.datastore
-import androidx.datastore.core.twoWayIpc.InterProcessCompletable
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.IpcUnit
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.multiprocess.ipcActions.ReadTextAction
+import androidx.datastore.testapp.multiprocess.ipcActions.SetTextAction
+import androidx.datastore.testapp.multiprocess.ipcActions.StorageVariant
+import androidx.datastore.testapp.multiprocess.ipcActions.createMultiProcessTestDatastore
+import androidx.datastore.testapp.multiprocess.ipcActions.datastore
+import androidx.datastore.testapp.twoWayIpc.InterProcessCompletable
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.IpcUnit
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
 import androidx.datastore.testing.TestMessageProto.FooProto
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CompletableDeferred
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessTestRule.kt b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessTestRule.kt
similarity index 92%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessTestRule.kt
rename to datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessTestRule.kt
index c2cbb5b..82df64d 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessTestRule.kt
+++ b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessTestRule.kt
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.multiprocess
+package androidx.datastore.testapp.multiprocess
 
-import androidx.datastore.core.twoWayIpc.TwoWayIpcConnection
-import androidx.datastore.core.twoWayIpc.TwoWayIpcService
-import androidx.datastore.core.twoWayIpc.TwoWayIpcService2
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcConnection
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcService
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcService2
 import androidx.test.platform.app.InstrumentationRegistry
 import java.util.concurrent.atomic.AtomicBoolean
 import kotlin.time.Duration.Companion.seconds
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt
similarity index 91%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt
rename to datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt
index 00442d7..9b07814 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt
+++ b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt
@@ -14,19 +14,22 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.multiprocess
+// Parcelize object is testing internal implementation of datastore-core library
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
 
-import androidx.datastore.core.multiprocess.ipcActions.ReadTextAction
-import androidx.datastore.core.multiprocess.ipcActions.SetTextAction
-import androidx.datastore.core.multiprocess.ipcActions.StorageVariant
-import androidx.datastore.core.multiprocess.ipcActions.createMultiProcessTestDatastore
-import androidx.datastore.core.multiprocess.ipcActions.datastore
-import androidx.datastore.core.twoWayIpc.CompositeServiceSubjectModel
-import androidx.datastore.core.twoWayIpc.InterProcessCompletable
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.IpcUnit
-import androidx.datastore.core.twoWayIpc.SubjectReadWriteProperty
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+package androidx.datastore.testapp.multiprocess
+
+import androidx.datastore.testapp.multiprocess.ipcActions.ReadTextAction
+import androidx.datastore.testapp.multiprocess.ipcActions.SetTextAction
+import androidx.datastore.testapp.multiprocess.ipcActions.StorageVariant
+import androidx.datastore.testapp.multiprocess.ipcActions.createMultiProcessTestDatastore
+import androidx.datastore.testapp.multiprocess.ipcActions.datastore
+import androidx.datastore.testapp.twoWayIpc.CompositeServiceSubjectModel
+import androidx.datastore.testapp.twoWayIpc.InterProcessCompletable
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.IpcUnit
+import androidx.datastore.testapp.twoWayIpc.SubjectReadWriteProperty
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
 import androidx.datastore.testing.TestMessageProto.FooProto
 import androidx.kruth.assertThat
 import kotlin.time.Duration.Companion.seconds
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/TwoWayIpcTest.kt b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/TwoWayIpcTest.kt
similarity index 95%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/TwoWayIpcTest.kt
rename to datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/TwoWayIpcTest.kt
index 3cf76e5..a6d77b6 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/TwoWayIpcTest.kt
+++ b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/TwoWayIpcTest.kt
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.multiprocess
+package androidx.datastore.testapp.multiprocess
 
 import android.os.Parcelable
-import androidx.datastore.core.twoWayIpc.CompositeServiceSubjectModel
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.twoWayIpc.CompositeServiceSubjectModel
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
 import com.google.common.truth.Truth.assertThat
 import kotlinx.parcelize.Parcelize
 import org.junit.Rule
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoOkioSerializer.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoOkioSerializer.kt
similarity index 95%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoOkioSerializer.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoOkioSerializer.kt
index cb319f1..6a291b8 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoOkioSerializer.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoOkioSerializer.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core
+package androidx.datastore.testapp
 
+import androidx.datastore.core.CorruptionException
 import androidx.datastore.core.okio.OkioSerializer
 import com.google.protobuf.ExtensionRegistryLite
 import com.google.protobuf.InvalidProtocolBufferException
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoSerializer.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoSerializer.kt
similarity index 92%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoSerializer.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoSerializer.kt
index 587f24f..f9eb2e3 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoSerializer.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoSerializer.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core
+package androidx.datastore.testapp
 
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.Serializer
 import com.google.protobuf.ExtensionRegistryLite
 import com.google.protobuf.InvalidProtocolBufferException
 import com.google.protobuf.MessageLite
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/CreateDatastoreAction.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/CreateDatastoreAction.kt
similarity index 87%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/CreateDatastoreAction.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/CreateDatastoreAction.kt
index 4890be9..cb3ef18 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/CreateDatastoreAction.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/CreateDatastoreAction.kt
@@ -14,24 +14,28 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.multiprocess.ipcActions
+// Parcelize object is testing internal implementation of datastore-core library
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
 
+package androidx.datastore.testapp.multiprocess.ipcActions
+
+import android.annotation.SuppressLint
 import android.os.Parcelable
 import androidx.datastore.core.CorruptionHandler
 import androidx.datastore.core.DataStore
 import androidx.datastore.core.DataStoreImpl
 import androidx.datastore.core.FileStorage
 import androidx.datastore.core.MultiProcessCoordinator
-import androidx.datastore.core.ProtoOkioSerializer
-import androidx.datastore.core.ProtoSerializer
 import androidx.datastore.core.Serializer
 import androidx.datastore.core.handlers.NoOpCorruptionHandler
 import androidx.datastore.core.okio.OkioSerializer
 import androidx.datastore.core.okio.OkioStorage
-import androidx.datastore.core.twoWayIpc.CompositeServiceSubjectModel
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.SubjectReadWriteProperty
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.ProtoOkioSerializer
+import androidx.datastore.testapp.ProtoSerializer
+import androidx.datastore.testapp.twoWayIpc.CompositeServiceSubjectModel
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.SubjectReadWriteProperty
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
 import androidx.datastore.testing.TestMessageProto.FooProto
 import com.google.protobuf.ExtensionRegistryLite
 import java.io.File
@@ -117,6 +121,7 @@
     )
 }
 
+@SuppressLint("BanParcelableUsage")
 @Parcelize
 private class CreateDatastoreAction(
     private val filePath: String,
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/ReadTextAction.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/ReadTextAction.kt
similarity index 67%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/ReadTextAction.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/ReadTextAction.kt
index eafa6b5..0162238 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/ReadTextAction.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/ReadTextAction.kt
@@ -14,17 +14,23 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.multiprocess.ipcActions
+// Parcelize object is testing internal implementation of datastore-core library
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
 
+package androidx.datastore.testapp.multiprocess.ipcActions
+
+import android.annotation.SuppressLint
 import android.os.Parcelable
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
 import kotlinx.coroutines.flow.first
 import kotlinx.parcelize.Parcelize
 
 @Parcelize
 internal class ReadTextAction : IpcAction<ReadTextAction.TextValue>() {
-    @Parcelize data class TextValue(val value: String) : Parcelable
+    @SuppressLint("BanParcelableUsage")
+    @Parcelize
+    data class TextValue(val value: String) : Parcelable
 
     override suspend fun invokeInRemoteProcess(subject: TwoWayIpcSubject): TextValue {
         return TextValue(subject.datastore.data.first().text)
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/SetTextAction.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/SetTextAction.kt
similarity index 73%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/SetTextAction.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/SetTextAction.kt
index ce98deb..929b23f 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/SetTextAction.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/SetTextAction.kt
@@ -14,15 +14,20 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.multiprocess.ipcActions
+// Parcelize object is testing internal implementation of datastore-core library
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
 
+package androidx.datastore.testapp.multiprocess.ipcActions
+
+import android.annotation.SuppressLint
 import android.os.Parcelable
-import androidx.datastore.core.twoWayIpc.InterProcessCompletable
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.IpcUnit
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.twoWayIpc.InterProcessCompletable
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.IpcUnit
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
 import kotlinx.parcelize.Parcelize
 
+@SuppressLint("BanParcelableUsage")
 @Parcelize
 internal class SetTextAction(
     private val value: String,
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/CompositeServiceSubjectModel.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/CompositeServiceSubjectModel.kt
similarity index 94%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/CompositeServiceSubjectModel.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/CompositeServiceSubjectModel.kt
index 3b8dd64..741bb88 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/CompositeServiceSubjectModel.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/CompositeServiceSubjectModel.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
 
+//noinspection BanConcurrentHashMap
 import java.util.concurrent.ConcurrentHashMap
 
 /**
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/InterProcessCompletable.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/InterProcessCompletable.kt
similarity index 96%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/InterProcessCompletable.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/InterProcessCompletable.kt
index 46eaffa..4e5d506 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/InterProcessCompletable.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/InterProcessCompletable.kt
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
 
+import android.annotation.SuppressLint
 import android.os.Parcelable
 import java.util.UUID
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.parcelize.Parcelize
 
 /** A [Parcelable] [CompletableDeferred] implementation that can be shared across processes. */
+@SuppressLint("BanParcelableUsage")
 @Parcelize
 internal class InterProcessCompletable<T : Parcelable>(
     private val key: String = UUID.randomUUID().toString(),
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcAction.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcAction.kt
similarity index 85%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcAction.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcAction.kt
index fa2de84..086510f 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcAction.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcAction.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
 
+import android.annotation.SuppressLint
 import android.os.Parcelable
 import kotlinx.parcelize.Parcelize
 
@@ -25,4 +26,4 @@
 }
 
 /** Utility object for [IpcAction]s that do not return a value. */
-@Parcelize object IpcUnit : Parcelable
+@SuppressLint("BanParcelableUsage") @Parcelize object IpcUnit : Parcelable
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcLogger.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcLogger.kt
similarity index 96%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcLogger.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcLogger.kt
index 628ff8b..278f16d 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcLogger.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcLogger.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
 
 import android.app.Application
 import android.os.Build
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcBus.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcBus.kt
similarity index 97%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcBus.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcBus.kt
index 4768068..3e84a15 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcBus.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcBus.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
 
 import android.os.Bundle
 import android.os.Handler
 import android.os.Looper
 import android.os.Message
 import android.os.Messenger
-import androidx.datastore.core.twoWayIpc.IpcLogger.log
+import androidx.datastore.testapp.twoWayIpc.IpcLogger.log
 import java.util.UUID
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CompletableDeferred
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcConnection.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcConnection.kt
similarity index 98%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcConnection.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcConnection.kt
index e153d5d..d37575e 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcConnection.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcConnection.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
 
 import android.content.ComponentName
 import android.content.Context
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcService.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcService.kt
similarity index 95%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcService.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcService.kt
index 9834950..2fb7ec2 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcService.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcService.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
 
 import android.content.Intent
 import android.os.Handler
@@ -44,7 +44,7 @@
  * It properly scopes those subjects and destroys their scopes when the Service is destroyed,
  * allowing tests to properly maintain resources.
  *
- * @see androidx.datastore.core.multiprocess.MultiProcessTestRule
+ * @see androidx.datastore.testapp.multiprocess.MultiProcessTestRule
  */
 open class TwoWayIpcService : LifecycleService() {
     private val subjects = mutableListOf<TwoWayIpcSubject>()
@@ -89,6 +89,7 @@
         )
 
     override fun onBind(intent: Intent): IBinder? {
+        super.onBind(intent)
         return messenger.binder
     }
 
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcSubject.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcSubject.kt
similarity index 98%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcSubject.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcSubject.kt
index 8c6cd3f..c6689a3 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcSubject.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcSubject.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
 
 import android.os.Bundle
 import android.os.Parcelable
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/proto/test.proto b/datastore/integration-tests/testapp/src/main/proto/test.proto
similarity index 100%
rename from datastore/datastore-core/src/androidInstrumentedTest/proto/test.proto
rename to datastore/integration-tests/testapp/src/main/proto/test.proto
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 9c8f089..d44adf5 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -48,8 +48,8 @@
 [0-9]+ problem.* found storing the configuration cache.*
 See the complete report at file://.*/build/reports/configuration\-cache/[^ ]*/[^ ]*/configuration\-cache\-report\.html
 # > Task :compose:ui:ui:processDebugAndroidTestManifest
-\$OUT_DIR/androidx/compose/runtime/runtime\-saveable/build/intermediates/tmp/manifest/androidTest/debug/tempFile[0-9]+ProcessTestManifest[0-9]+\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
-\$OUT_DIR/androidx/compose/ui/ui\-tooling/build/intermediates/tmp/manifest/androidTest/debug/tempFile[0-9]+ProcessTestManifest[0-9]+\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
+\$OUT_DIR/androidx/compose/runtime/runtime\-saveable/build/intermediates/tmp/manifest/androidTest/release/tempFile[0-9]+ProcessTestManifest[0-9]+\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
+\$OUT_DIR/androidx/compose/ui/ui\-tooling/build/intermediates/tmp/manifest/androidTest/release/tempFile[0-9]+ProcessTestManifest[0-9]+\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
 # > Task :buildSrc:build UP-TO-DATE
 A fine\-grained performance profile is available\: use the \-\-scan option\.
 # > Task :docs
diff --git a/development/update_kotlin.sh b/development/update_kotlin.sh
index b6f492a..eff00fd 100755
--- a/development/update_kotlin.sh
+++ b/development/update_kotlin.sh
@@ -2,14 +2,12 @@
 set -e
 
 KOTLIN_VERSION="$1"
+KSP_VERSION="$2"
 
-ALLOW_JETBRAINS_DEV=""
-for arg in "$@"
-do
-    if [ "$arg" == "--allow-jetbrains-dev" ]; then
-      ALLOW_JETBRAINS_DEV="--allow-jetbrains-dev"
-    fi
-done
+if [[ $# -eq 0 ]] ; then
+    echo "Usage ./development/update_kotlin.sh <kotlin_version> [<ksp_version>]"
+    exit 1
+fi
 
 # Download maven artifacts
 ARTIFACTS_TO_DOWNLOAD="org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION,"
@@ -38,7 +36,39 @@
 ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-native-prebuilt:$KOTLIN_VERSION:[email protected],"
 ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-native-prebuilt:$KOTLIN_VERSION:[email protected],"
 
-./development/importMaven/importMaven.sh "$ALLOW_JETBRAINS_DEV" "$ARTIFACTS_TO_DOWNLOAD"
+if [ "$KSP_VERSION" ]; then
+    ARTIFACTS_TO_DOWNLOAD+="com.google.devtools.ksp:symbol-processing:$KSP_VERSION,"
+    ARTIFACTS_TO_DOWNLOAD+="com.google.devtools.ksp:symbol-processing-api:$KSP_VERSION,"
+    ARTIFACTS_TO_DOWNLOAD+="com.google.devtools.ksp:symbol-processing-cmdline:$KSP_VERSION,"
+    ARTIFACTS_TO_DOWNLOAD+="com.google.devtools.ksp:symbol-processing-gradle-plugin:$KSP_VERSION,"
+    ARTIFACTS_TO_DOWNLOAD+="com.google.devtools.ksp:symbol-processing-aa-embeddable:$KSP_VERSION,"
+fi
 
- ./development/importMaven/importMaven.sh import-konan-binaries --konan-compiler-version "$KOTLIN_VERSION"
+./development/importMaven/importMaven.sh "$ARTIFACTS_TO_DOWNLOAD"
 
+# symlink native compiler prebuilt archives from prebuilts/androidx/external to prebuilts/androidx/konan
+# to make KonanPrebuiltsSetup.kt work.
+rm -fr "../../prebuilts/androidx/konan/nativeCompilerPrebuilts/releases"
+
+REAL_NATIVE_PREBUILT_DIR="../../../../../external/org/jetbrains/kotlin/kotlin-native-prebuilt/$KOTLIN_VERSION/"
+
+LINUX_DIR="../../prebuilts/androidx/konan/nativeCompilerPrebuilts/releases/$KOTLIN_VERSION/linux-x86_64"
+mkdir -p "$LINUX_DIR"
+ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-linux-x86_64.tar.gz" \
+    "$LINUX_DIR/kotlin-native-prebuilt-linux-x86_64-$KOTLIN_VERSION.tar.gz"
+ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-linux-x86_64.tar.gz.asc" \
+    "$LINUX_DIR/kotlin-native-prebuilt-linux-x86_64-$KOTLIN_VERSION.tar.gz.asc"
+
+MAC_ARM_DIR="../../prebuilts/androidx/konan/nativeCompilerPrebuilts/releases/$KOTLIN_VERSION/macos-aarch64"
+mkdir -p "$MAC_ARM_DIR"
+ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-macos-aarch64.tar.gz" \
+    "$MAC_ARM_DIR/kotlin-native-prebuilt-macos-aarch64-$KOTLIN_VERSION.tar.gz"
+ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-macos-aarch64.tar.gz.asc" \
+    "$MAC_ARM_DIR/kotlin-native-prebuilt-macos-aarch64-$KOTLIN_VERSION.tar.gz.asc"
+
+MAC_X86_DIR="../../prebuilts/androidx/konan/nativeCompilerPrebuilts/releases/$KOTLIN_VERSION/macos-x86_64"
+mkdir -p "$MAC_X86_DIR"
+ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-macos-x86_64.tar.gz" \
+    "$MAC_X86_DIR/kotlin-native-prebuilt-macos-x86_64-$KOTLIN_VERSION.tar.gz"
+ln -s -f "$REAL_NATIVE_PREBUILT_DIR/kotlin-native-prebuilt-$KOTLIN_VERSION-macos-x86_64.tar.gz.asc" \
+    "$MAC_X86_DIR/kotlin-native-prebuilt-macos-x86_64-$KOTLIN_VERSION.tar.gz.asc"
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index f0650b2..d1966329 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -223,31 +223,31 @@
     docs("androidx.media2:media2-widget:1.3.0")
     docs("androidx.media:media:1.7.0")
     // androidx.media3 is not hosted in androidx
-    docsWithoutApiSince("androidx.media3:media3-cast:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-common:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-container:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-database:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-datasource:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-datasource-cronet:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-datasource-okhttp:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-datasource-rtmp:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-decoder:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-effect:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-dash:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-hls:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-ima:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-rtsp:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-smoothstreaming:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-workmanager:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-extractor:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-muxer:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-session:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-test-utils:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-test-utils-robolectric:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-transformer:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-ui:1.4.0")
-    docsWithoutApiSince("androidx.media3:media3-ui-leanback:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-cast:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-common:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-container:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-database:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-datasource:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-datasource-cronet:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-datasource-okhttp:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-datasource-rtmp:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-decoder:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-effect:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-dash:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-hls:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-ima:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-rtsp:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-smoothstreaming:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-workmanager:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-extractor:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-muxer:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-session:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-test-utils:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-test-utils-robolectric:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-transformer:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-ui:1.4.1")
+    docsWithoutApiSince("androidx.media3:media3-ui-leanback:1.4.1")
     docs("androidx.mediarouter:mediarouter:1.7.0")
     docs("androidx.mediarouter:mediarouter-testing:1.7.0")
     docs("androidx.metrics:metrics-performance:1.0.0-beta01")
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index 5704cee..b4d4e4f 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -52,6 +52,8 @@
     docs(project(":bluetooth:bluetooth-testing"))
     docs(project(":browser:browser"))
     docs(project(":camera:camera-camera2"))
+    docs(project(":camera:camera-compose"))
+    samples(project(":camera:camera-compose:camera-compose-samples"))
     docs(project(":camera:camera-core"))
     docs(project(":camera:camera-effects"))
     docs(project(":camera:camera-effects-still-portrait"))
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
index 4d42ff9..a35d75c 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
@@ -18,7 +18,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="emoji_category_recent" msgid="7142376595414250279">"المستخدمة حديثًا"</string>
-    <string name="emoji_category_emotions" msgid="1570830970240985537">"الوجوه المبتسمة والرموز التعبيرية"</string>
+    <string name="emoji_category_emotions" msgid="1570830970240985537">"الوجوه المبتسمة ورموز الإيموجي"</string>
     <string name="emoji_category_people" msgid="7968173366822927025">"الأشخاص"</string>
     <string name="emoji_category_animals_nature" msgid="4640771324837307541">"الحيوانات والطبيعة"</string>
     <string name="emoji_category_food_drink" msgid="1189971856721244395">"المأكولات والمشروبات"</string>
@@ -27,11 +27,11 @@
     <string name="emoji_category_objects" msgid="6106115586332708067">"عناصر متنوعة"</string>
     <string name="emoji_category_symbols" msgid="5626171724310261787">"الرموز"</string>
     <string name="emoji_category_flags" msgid="6185639503532784871">"الأعلام"</string>
-    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"لا تتوفر أي رموز تعبيرية."</string>
-    <string name="emoji_empty_recent_category" msgid="7863877827879290200">"لم تستخدم أي رموز تعبيرية حتى الآن."</string>
-    <string name="emoji_bidirectional_switcher_content_desc" msgid="5084600168354220605">"مفتاح ثنائي الاتجاه للرموز التعبيرية"</string>
+    <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"لا تتوفر أي رموز إيموجي."</string>
+    <string name="emoji_empty_recent_category" msgid="7863877827879290200">"لم تستخدم أي رموز إيموجي حتى الآن."</string>
+    <string name="emoji_bidirectional_switcher_content_desc" msgid="5084600168354220605">"مفتاح ثنائي الاتجاه لرموز الإيموجي"</string>
     <string name="emoji_bidirectional_switcher_clicked_desc" msgid="5055290162204827523">"تم تغيير اتجاه الإيموجي"</string>
-    <string name="emoji_variant_selector_content_desc" msgid="2898934883418401376">"أداة اختيار الرموز التعبيرية"</string>
+    <string name="emoji_variant_selector_content_desc" msgid="2898934883418401376">"أداة اختيار رموز الإيموجي"</string>
     <string name="emoji_variant_content_desc_template" msgid="6381933050671041489">"‏%1$s و%2$s"</string>
     <string name="emoji_skin_tone_shadow_content_desc" msgid="1759906883307507376">"الظل"</string>
     <string name="emoji_skin_tone_light_content_desc" msgid="1052239040923092881">"بشرة فاتحة"</string>
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 5165817..692ca84 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -44,7 +44,7 @@
 kotlin17 = "1.7.10"
 kotlin18 = "1.8.22"
 kotlin19 = "1.9.24"
-kotlin = "2.0.10"
+kotlin = "2.0.20"
 kotlinBenchmark = "0.4.11"
 kotlinGradlePluginAnnotations = "1.9.24"
 kotlinGradlePluginApi = "1.9.24"
@@ -53,7 +53,7 @@
 kotlinNativeUtils = "1.9.24"
 kotlinSerialization = "1.6.3"
 kotlinToolingCore = "1.9.24"
-ksp = "2.0.10-1.0.24"
+ksp = "2.0.20-1.0.24"
 ktfmt = "0.50"
 leakcanary = "2.13"
 media3 = "1.1.0"
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
index 966c457..b417a45 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
@@ -49,36 +49,32 @@
             var surfaceView: SurfaceView? = null
             val destroyLatch = CountDownLatch(1)
             val scenario =
-                ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
-                    .moveToState(Lifecycle.State.CREATED)
-                    .onActivity {
-                        it.setDestroyCallback { destroyLatch.countDown() }
-                        val callback =
-                            object : SurfaceHolder.Callback {
-                                override fun surfaceCreated(sh: SurfaceHolder) {
-                                    surfaceView = it.mSurfaceView
-                                    onSurfaceCreated(surfaceView!!, setupLatch)
-                                }
-
-                                override fun surfaceChanged(
-                                    holder: SurfaceHolder,
-                                    format: Int,
-                                    width: Int,
-                                    height: Int
-                                ) {
-                                    // NO-OP
-                                }
-
-                                override fun surfaceDestroyed(holder: SurfaceHolder) {
-                                    // NO-OP
-                                }
+                ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java).onActivity {
+                    it.setDestroyCallback { destroyLatch.countDown() }
+                    val callback =
+                        object : SurfaceHolder.Callback {
+                            override fun surfaceCreated(sh: SurfaceHolder) {
+                                surfaceView = it.mSurfaceView
+                                onSurfaceCreated(surfaceView!!, setupLatch)
                             }
 
-                        it.addSurface(it.mSurfaceView, callback)
-                        surfaceView = it.mSurfaceView
-                    }
+                            override fun surfaceChanged(
+                                holder: SurfaceHolder,
+                                format: Int,
+                                width: Int,
+                                height: Int
+                            ) {
+                                // NO-OP
+                            }
 
-            scenario.moveToState(Lifecycle.State.RESUMED)
+                            override fun surfaceDestroyed(holder: SurfaceHolder) {
+                                // NO-OP
+                            }
+                        }
+
+                    it.addSurface(it.mSurfaceView, callback)
+                    surfaceView = it.mSurfaceView
+                }
 
             Assert.assertTrue(setupLatch.await(3000, TimeUnit.MILLISECONDS))
             val coords = intArrayOf(0, 0)
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlWrapperTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlWrapperTest.kt
index 4a3e5b9..dec948e 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlWrapperTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlWrapperTest.kt
@@ -137,9 +137,7 @@
     fun testSurfaceTransactionOnCompleteCallback() {
         val listener = TransactionOnCompleteListener()
 
-        val scenario =
-            ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
-                .moveToState(Lifecycle.State.CREATED)
+        val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
 
         val destroyLatch = CountDownLatch(1)
         try {
@@ -150,8 +148,6 @@
                     .commit()
             }
 
-            scenario.moveToState(Lifecycle.State.RESUMED)
-
             listener.mLatch.await(3, TimeUnit.SECONDS)
             assertEquals(0, listener.mLatch.count)
             assertTrue(listener.mCallbackTime > 0)
@@ -167,9 +163,7 @@
     fun testSurfaceTransactionOnCommitCallback() {
         val listener = TransactionOnCommitListener()
 
-        val scenario =
-            ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
-                .moveToState(Lifecycle.State.CREATED)
+        val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
 
         val destroyLatch = CountDownLatch(1)
         try {
@@ -179,7 +173,6 @@
                     .addTransactionCommittedListener(executor!!, listener)
                     .commit()
             }
-            scenario.moveToState(Lifecycle.State.RESUMED)
 
             listener.mLatch.await(3, TimeUnit.SECONDS)
             assertEquals(0, listener.mLatch.count)
@@ -197,9 +190,7 @@
         val listener = TransactionOnCommitListener()
         val listener2 = TransactionOnCommitListener()
 
-        val scenario =
-            ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
-                .moveToState(Lifecycle.State.CREATED)
+        val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
 
         val destroyLatch = CountDownLatch(1)
         try {
@@ -211,8 +202,6 @@
                     .commit()
             }
 
-            scenario.moveToState(Lifecycle.State.RESUMED)
-
             listener.mLatch.await(3, TimeUnit.SECONDS)
             listener2.mLatch.await(3, TimeUnit.SECONDS)
 
@@ -234,9 +223,7 @@
         val listener1 = TransactionOnCommitListener()
         val listener2 = TransactionOnCompleteListener()
 
-        val scenario =
-            ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
-                .moveToState(Lifecycle.State.CREATED)
+        val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
 
         val destroyLatch = CountDownLatch(1)
         try {
@@ -248,8 +235,6 @@
                     .commit()
             }
 
-            scenario.moveToState(Lifecycle.State.RESUMED)
-
             listener1.mLatch.await(3, TimeUnit.SECONDS)
             listener2.mLatch.await(3, TimeUnit.SECONDS)
 
@@ -852,39 +837,37 @@
         var scCompat: SurfaceControlWrapper? = null
         val listener = TransactionOnCompleteListener()
         val scenario =
-            ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
-                .moveToState(Lifecycle.State.CREATED)
-                .onActivity {
-                    val callback =
-                        object : SurfaceHolderCallback() {
-                            override fun surfaceCreated(sh: SurfaceHolder) {
-                                scCompat =
-                                    SurfaceControlWrapper.Builder()
-                                        .setParent(it.getSurfaceView().holder.surface)
-                                        .setDebugName("SurfaceControlCompatTest")
-                                        .build()
+            ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java).onActivity {
+                val callback =
+                    object : SurfaceHolderCallback() {
+                        override fun surfaceCreated(sh: SurfaceHolder) {
+                            scCompat =
+                                SurfaceControlWrapper.Builder()
+                                    .setParent(it.getSurfaceView().holder.surface)
+                                    .setDebugName("SurfaceControlCompatTest")
+                                    .build()
 
-                                // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
-                                val buffer =
-                                    SurfaceControlUtils.getSolidBuffer(
-                                        SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
-                                        SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
-                                        Color.BLUE
-                                    )
+                            // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
+                            val buffer =
+                                SurfaceControlUtils.getSolidBuffer(
+                                    SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
+                                    SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
+                                    Color.BLUE
+                                )
 
-                                SurfaceControlWrapper.Transaction()
-                                    .addTransactionCompletedListener(listener)
-                                    .setBuffer(scCompat!!, buffer)
-                                    .setVisibility(scCompat!!, true)
-                                    .setCrop(scCompat!!, Rect(20, 30, 90, 60))
-                                    .commit()
-                            }
+                            SurfaceControlWrapper.Transaction()
+                                .addTransactionCompletedListener(listener)
+                                .setBuffer(scCompat!!, buffer)
+                                .setVisibility(scCompat!!, true)
+                                .setCrop(scCompat!!, Rect(20, 30, 90, 60))
+                                .commit()
                         }
+                    }
 
-                    it.addSurface(it.mSurfaceView, callback)
-                }
+                it.addSurface(it.mSurfaceView, callback)
+            }
 
-        scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
+        scenario.onActivity {
             assert(listener.mLatch.await(3000, TimeUnit.MILLISECONDS))
             SurfaceControlUtils.validateOutput { bitmap ->
                 val coord = intArrayOf(0, 0)
diff --git a/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt
index 368f7b1..c7f7c75 100644
--- a/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt
+++ b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt
@@ -229,6 +229,29 @@
         }
     }
 
+    @Test
+    fun emptyPolygonTest() {
+        val poly = RoundedPolygon(6, radius = 0f, rounding = CornerRounding(0.1f))
+        assert(poly.cubics.size == 1)
+
+        val stillEmpty = poly.transformed(scaleTransform(10f, 20f))
+        assert(stillEmpty.cubics.size == 1)
+        assert(stillEmpty.cubics.first().zeroLength())
+    }
+
+    @Test
+    fun emptySideTest() {
+        val poly1 =
+            RoundedPolygon(
+                floatArrayOf(0f, 0f, 1f, 0f, 1f, 0f, 0f, 1f), // Triangle with one point repeated
+            )
+        val poly2 =
+            RoundedPolygon(
+                floatArrayOf(0f, 0f, 1f, 0f, 0f, 1f), // Triangle
+            )
+        assertCubicListsEqualish(poly1.cubics, poly2.cubics)
+    }
+
     private fun nonzeroCubics(original: List<Cubic>): List<Cubic> {
         val result = mutableListOf<Cubic>()
         for (i in original.indices) {
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
index 051588a..5ada39e 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
@@ -74,7 +74,7 @@
                 }
             }
         }
-        if (lastCubic != null && firstCubic != null)
+        if (lastCubic != null && firstCubic != null) {
             add(
                 Cubic(
                     lastCubic.anchor0X,
@@ -87,6 +87,21 @@
                     firstCubic.anchor0Y
                 )
             )
+        } else {
+            // Empty / 0-sized polygon.
+            add(
+                Cubic(
+                    centerX,
+                    centerY,
+                    centerX,
+                    centerY,
+                    centerX,
+                    centerY,
+                    centerX,
+                    centerY,
+                )
+            )
+        }
     }
 
     init {
@@ -100,12 +115,11 @@
                     abs(cubic.anchor0Y - prevCubic.anchor1Y) > DistanceEpsilon
             ) {
                 debugLog("RoundedPolygon") {
-                    "Ix: $index | (${cubic.anchor0X},${cubic.anchor0Y}) vs " + "$prevCubic"
+                    "Ix: $index | (${cubic.anchor0X},${cubic.anchor0Y}) vs $prevCubic"
                 }
                 throw IllegalArgumentException(
-                    "RoundedPolygon must be contiguous, with the " +
-                        "anchor points of all curves matching the anchor points of the preceding " +
-                        "and succeeding cubics"
+                    "RoundedPolygon must be contiguous, with the anchor points of all curves " +
+                        "matching the anchor points of the preceding and succeeding cubics"
                 )
             }
             prevCubic = cubic
@@ -483,27 +497,51 @@
     val p2: Point,
     val rounding: CornerRounding? = null
 ) {
-    val d1 = (p0 - p1).getDirection()
-    val d2 = (p2 - p1).getDirection()
-    val cornerRadius = rounding?.radius ?: 0f
-    val smoothing = rounding?.smoothing ?: 0f
+    val d1: Point
+    val d2: Point
+    val cornerRadius: Float
+    val smoothing: Float
+    val cosAngle: Float
+    val sinAngle: Float
+    val expectedRoundCut: Float
 
-    // cosine of angle at p1 is dot product of unit vectors to the other two vertices
-    val cosAngle = d1.dotProduct(d2)
+    init {
+        val v01 = p0 - p1
+        val v21 = p2 - p1
+        val d01 = v01.getDistance()
+        val d21 = v21.getDistance()
+        if (d01 > 0f && d21 > 0f) {
+            d1 = v01 / d01
+            d2 = v21 / d21
+            cornerRadius = rounding?.radius ?: 0f
+            smoothing = rounding?.smoothing ?: 0f
 
-    // identity: sin^2 + cos^2 = 1
-    // sinAngle gives us the intersection
-    val sinAngle = sqrt(1 - square(cosAngle))
+            // cosine of angle at p1 is dot product of unit vectors to the other two vertices
+            cosAngle = d1.dotProduct(d2)
 
-    // How much we need to cut, as measured on a side, to get the required radius
-    // calculating where the rounding circle hits the edge
-    // This uses the identity of tan(A/2) = sinA/(1 + cosA), where tan(A/2) = radius/cut
-    val expectedRoundCut =
-        if (sinAngle > 1e-3) {
-            cornerRadius * (cosAngle + 1) / sinAngle
+            // identity: sin^2 + cos^2 = 1
+            // sinAngle gives us the intersection
+            sinAngle = sqrt(1 - square(cosAngle))
+            // How much we need to cut, as measured on a side, to get the required radius
+            // calculating where the rounding circle hits the edge
+            // This uses the identity of tan(A/2) = sinA/(1 + cosA), where tan(A/2) = radius/cut
+            expectedRoundCut =
+                if (sinAngle > 1e-3) {
+                    cornerRadius * (cosAngle + 1) / sinAngle
+                } else {
+                    0f
+                }
         } else {
-            0f
+            // One (or both) of the sides is empty, not much we can do.
+            d1 = Point(0f, 0f)
+            d2 = Point(0f, 0f)
+            cornerRadius = 0f
+            smoothing = 0f
+            cosAngle = 0f
+            sinAngle = 0f
+            expectedRoundCut = 0f
         }
+    }
 
     // smoothing changes the actual cut. 0 is same as expectedRoundCut, 1 doubles it
     val expectedCut: Float
diff --git a/libraryversions.toml b/libraryversions.toml
index 85d5aab..b33669b 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -1,5 +1,5 @@
 [versions]
-ACTIVITY = "1.10.0-beta01"
+ACTIVITY = "1.10.0-alpha02"
 ANNOTATION = "1.9.0-alpha02"
 ANNOTATION_EXPERIMENTAL = "1.5.0-alpha01"
 APPCOMPAT = "1.8.0-alpha01"
@@ -96,7 +96,7 @@
 MEDIA = "1.7.0-rc01"
 MEDIAROUTER = "1.8.0-alpha01"
 METRICS = "1.0.0-beta02"
-NAVIGATION = "2.8.0-rc01"
+NAVIGATION = "2.9.0-alpha01"
 PAGING = "3.4.0-alpha01"
 PALETTE = "1.1.0-alpha01"
 PDF = "1.0.0-alpha02"
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
index dee1a74..4799f00 100644
--- a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
@@ -86,6 +86,7 @@
                     // This issue is only enabled when `-Pandroidx.useJSpecifyAnnotations=true`.
                     JSpecifyNullnessMigration.ISSUE,
                     TypeMirrorToString.ISSUE,
+                    BanNullMarked.ISSUE,
                 )
             }
     }
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanNullMarked.kt b/lint-checks/src/main/java/androidx/build/lint/BanNullMarked.kt
new file mode 100644
index 0000000..9b735cc
--- /dev/null
+++ b/lint-checks/src/main/java/androidx/build/lint/BanNullMarked.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 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.build.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Incident
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UAnnotation
+
+class BanNullMarked : Detector(), Detector.UastScanner {
+
+    override fun getApplicableUastTypes() = listOf(UAnnotation::class.java)
+
+    override fun createUastHandler(context: JavaContext): UElementHandler {
+        return AnnotationChecker(context)
+    }
+
+    private inner class AnnotationChecker(val context: JavaContext) : UElementHandler() {
+        override fun visitAnnotation(node: UAnnotation) {
+            if (node.qualifiedName != "org.jspecify.annotations.NullMarked") return
+
+            val incident =
+                Incident(context)
+                    .issue(ISSUE)
+                    .location(context.getLocation(node))
+                    .scope(node)
+                    .message("Should not use @NullMarked annotation")
+            context.report(incident)
+        }
+    }
+
+    companion object {
+        val ISSUE =
+            Issue.create(
+                "BanNullMarked",
+                "Should not use @NullMarked annotation",
+                "Usage of the @NullMarked annotation is not allowed because lint and metalava do not support it",
+                Category.CORRECTNESS,
+                5,
+                Severity.ERROR,
+                Implementation(BanNullMarked::class.java, Scope.JAVA_FILE_SCOPE)
+            )
+    }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanNullMarkedTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanNullMarkedTest.kt
new file mode 100644
index 0000000..c16ea81
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanNullMarkedTest.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2024 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.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanNullMarkedTest :
+    AbstractLintDetectorTest(
+        useDetector = BanNullMarked(),
+        useIssues = listOf(BanNullMarked.ISSUE),
+        stubs = arrayOf(nullMarkedStub)
+    ) {
+    @Test
+    fun `Usage of NullMarked in a package-info file`() {
+        val input =
+            java(
+                """
+                    @NullMarked
+                    package test.pkg;
+
+                    import org.jspecify.annotations.NullMarked;
+                """
+                    .trimIndent()
+            )
+
+        val expected =
+            """
+                src/test/pkg/package-info.java:1: Error: Should not use @NullMarked annotation [BanNullMarked]
+                @NullMarked
+                ~~~~~~~~~~~
+                1 errors, 0 warnings
+            """
+                .trimIndent()
+
+        check(input).expect(expected)
+    }
+
+    @Test
+    fun `Usage of NullMarked on a class`() {
+        val input =
+            java(
+                """
+                    package test.pkg;
+
+                    import org.jspecify.annotations.NullMarked;
+
+                    @NullMarked
+                    public class Foo {}
+                """
+                    .trimIndent()
+            )
+
+        val expected =
+            """
+                src/test/pkg/Foo.java:5: Error: Should not use @NullMarked annotation [BanNullMarked]
+                @NullMarked
+                ~~~~~~~~~~~
+                1 errors, 0 warnings
+            """
+                .trimIndent()
+
+        check(input).expect(expected)
+    }
+
+    @Test
+    fun `Usage of NullMarked on a method`() {
+        val input =
+            java(
+                """
+                    package test.pkg;
+
+                    import org.jspecify.annotations.NullMarked;
+
+                    public class Foo {
+                        @NullMarked
+                        public void foo() {}
+                    }
+                """
+                    .trimIndent()
+            )
+
+        val expected =
+            """
+                src/test/pkg/Foo.java:6: Error: Should not use @NullMarked annotation [BanNullMarked]
+                    @NullMarked
+                    ~~~~~~~~~~~
+                1 errors, 0 warnings
+            """
+                .trimIndent()
+
+        check(input).expect(expected)
+    }
+
+    @Test
+    fun `Usage of NullMarked on a constructor`() {
+        val input =
+            java(
+                """
+                    package test.pkg;
+
+                    import org.jspecify.annotations.NullMarked;
+
+                    public class Foo {
+                        @NullMarked
+                        public Foo() {}
+                    }
+                """
+                    .trimIndent()
+            )
+
+        val expected =
+            """
+                src/test/pkg/Foo.java:6: Error: Should not use @NullMarked annotation [BanNullMarked]
+                    @NullMarked
+                    ~~~~~~~~~~~
+                1 errors, 0 warnings
+            """
+                .trimIndent()
+
+        check(input).expect(expected)
+    }
+
+    companion object {
+        private val nullMarkedStub =
+            java(
+                """
+                    package org.jspecify.annotations;
+
+                    import java.lang.annotation.ElementType;
+                    import java.lang.annotation.Target;
+
+                    @Target({ElementType.MODULE, ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
+                    public @interface NullMarked {}
+                """
+                    .trimIndent()
+            )
+    }
+}
diff --git a/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt b/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt
index 095938c..f063877 100644
--- a/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt
+++ b/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt
@@ -15,6 +15,7 @@
  */
 package androidx.metrics.performance.test
 
+import android.os.Build.VERSION.SDK_INT
 import android.view.Choreographer
 import androidx.metrics.performance.FrameData
 import androidx.metrics.performance.FrameDataApi24
@@ -36,6 +37,7 @@
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertTrue
+import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -107,6 +109,7 @@
 
     @Test
     fun testEnable() {
+        assumeTrue("Skip running an API 26 as it is flaky b/361092826", SDK_INT != 26)
         assertTrue(jankStats.isTrackingEnabled)
         jankStats.isTrackingEnabled = false
         assertFalse(jankStats.isTrackingEnabled)
diff --git a/paging/paging-common/bcv/native/current.txt b/paging/paging-common/bcv/native/current.txt
index 64890c9..61b670d 100644
--- a/paging/paging-common/bcv/native/current.txt
+++ b/paging/paging-common/bcv/native/current.txt
@@ -70,8 +70,6 @@
     final fun unregisterInvalidatedCallback(kotlin/Function0<kotlin/Unit>) // androidx.paging/PagingSource.unregisterInvalidatedCallback|unregisterInvalidatedCallback(kotlin.Function0<kotlin.Unit>){}[0]
 
     sealed class <#A1: kotlin/Any, #B1: kotlin/Any> LoadResult { // androidx.paging/PagingSource.LoadResult|null[0]
-        constructor <init>() // androidx.paging/PagingSource.LoadResult.<init>|<init>(){}[0]
-
         final class <#A2: kotlin/Any, #B2: kotlin/Any> Error : androidx.paging/PagingSource.LoadResult<#A2, #B2> { // androidx.paging/PagingSource.LoadResult.Error|null[0]
             constructor <init>(kotlin/Throwable) // androidx.paging/PagingSource.LoadResult.Error.<init>|<init>(kotlin.Throwable){}[0]
 
@@ -125,8 +123,6 @@
     }
 
     sealed class <#A1: kotlin/Any> LoadParams { // androidx.paging/PagingSource.LoadParams|null[0]
-        constructor <init>(kotlin/Int, kotlin/Boolean) // androidx.paging/PagingSource.LoadParams.<init>|<init>(kotlin.Int;kotlin.Boolean){}[0]
-
         abstract val key // androidx.paging/PagingSource.LoadParams.key|{}key[0]
             abstract fun <get-key>(): #A1? // androidx.paging/PagingSource.LoadParams.key.<get-key>|<get-key>(){}[0]
         final val loadSize // androidx.paging/PagingSource.LoadParams.loadSize|{}loadSize[0]
@@ -172,8 +168,6 @@
     }
 
     sealed class MediatorResult { // androidx.paging/RemoteMediator.MediatorResult|null[0]
-        constructor <init>() // androidx.paging/RemoteMediator.MediatorResult.<init>|<init>(){}[0]
-
         final class Error : androidx.paging/RemoteMediator.MediatorResult { // androidx.paging/RemoteMediator.MediatorResult.Error|null[0]
             constructor <init>(kotlin/Throwable) // androidx.paging/RemoteMediator.MediatorResult.Error.<init>|<init>(kotlin.Throwable){}[0]
 
@@ -342,8 +336,6 @@
 }
 
 sealed class <#A: kotlin/Any> androidx.paging/PagingDataEvent { // androidx.paging/PagingDataEvent|null[0]
-    constructor <init>() // androidx.paging/PagingDataEvent.<init>|<init>(){}[0]
-
     final class <#A1: kotlin/Any> Append : androidx.paging/PagingDataEvent<#A1> { // androidx.paging/PagingDataEvent.Append|null[0]
         constructor <init>(kotlin/Int, kotlin.collections/List<#A1>, kotlin/Int, kotlin/Int) // androidx.paging/PagingDataEvent.Append.<init>|<init>(kotlin.Int;kotlin.collections.List<1:0>;kotlin.Int;kotlin.Int){}[0]
 
@@ -423,8 +415,6 @@
 }
 
 sealed class androidx.paging/LoadState { // androidx.paging/LoadState|null[0]
-    constructor <init>(kotlin/Boolean) // androidx.paging/LoadState.<init>|<init>(kotlin.Boolean){}[0]
-
     final val endOfPaginationReached // androidx.paging/LoadState.endOfPaginationReached|{}endOfPaginationReached[0]
         final fun <get-endOfPaginationReached>(): kotlin/Boolean // androidx.paging/LoadState.endOfPaginationReached.<get-endOfPaginationReached>|<get-endOfPaginationReached>(){}[0]
 
diff --git a/paging/paging-common/build.gradle b/paging/paging-common/build.gradle
index 2bfc291..1c5792b 100644
--- a/paging/paging-common/build.gradle
+++ b/paging/paging-common/build.gradle
@@ -27,9 +27,9 @@
 import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
 import org.jetbrains.kotlin.konan.target.Family
 
+
 plugins {
     id("AndroidXPlugin")
-    id("com.android.library")
 }
 
 androidXMultiplatform {
@@ -39,7 +39,14 @@
     ios()
     watchos()
     tvos()
-    android()
+    androidLibrary {
+        namespace = "androidx.paging.common"
+        withAndroidTestOnDeviceBuilder {
+            it.compilationName = "instrumentedTest"
+            it.defaultSourceSetName = "androidInstrumentedTest"
+            it.sourceSetTreeName = "test"
+        }
+    }
 
     defaultPlatform(PlatformIdentifier.JVM)
 
@@ -150,7 +157,3 @@
     metalavaK2UastEnabled = false
     samples(project(":paging:paging-samples"))
 }
-
-android {
-    namespace "androidx.paging.common"
-}
diff --git a/paging/paging-common/lint-baseline.xml b/paging/paging-common/lint-baseline.xml
new file mode 100644
index 0000000..c360e17
--- /dev/null
+++ b/paging/paging-common/lint-baseline.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.7.0-alpha02" type="baseline" client="gradle" dependencies="false" name="AGP (8.7.0-alpha02)" variant="all" version="8.7.0-alpha02">
+
+    <issue
+        id="NewApi"
+        message="This Kotlin extension function will be hidden by `java.util.SequencedCollection` starting in API 35"
+        errorLine1="            @OptIn(ExperimentalStdlibApi::class) repeat(result.size) { _hints.removeFirst() }"
+        errorLine2="                                                                       ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/commonTest/kotlin/androidx/paging/PagingDataPresenterTest.kt"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="This Kotlin extension function will be hidden by `java.util.SequencedCollection` starting in API 35"
+        errorLine1="            @OptIn(ExperimentalStdlibApi::class) repeat(result.size) { _hints.removeFirst() }"
+        errorLine2="                                                                       ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/commonTest/kotlin/androidx/paging/PagingDataPresenterTest.kt"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="This Kotlin extension function will be hidden by `java.util.SequencedCollection` starting in API 35"
+        errorLine1="    repeat(data.size) { removeFirst() }"
+        errorLine2="                        ~~~~~~~~~~~~~">
+        <location
+            file="src/commonTest/kotlin/androidx/paging/TestUtils.kt"/>
+    </issue>
+
+    <issue
+        id="BanThreadSleep"
+        message="Uses Thread.sleep()"
+        errorLine1="                Thread.sleep(1000)"
+        errorLine2="                       ~~~~~">
+        <location
+            file="src/commonJvmAndroidTest/kotlin/androidx/paging/PagedListTest.kt"/>
+    </issue>
+
+    <issue
+        id="BanThreadSleep"
+        message="Uses Thread.sleep()"
+        errorLine1="                    @Suppress(&quot;BlockingMethodInNonBlockingContext&quot;) Thread.sleep(100)"
+        errorLine2="                                                                           ~~~~~">
+        <location
+            file="src/commonJvmAndroidTest/kotlin/androidx/paging/SingleRunnerTest.kt"/>
+    </issue>
+
+</issues>
diff --git a/paging/paging-testing/build.gradle b/paging/paging-testing/build.gradle
index 9212202..50837cb 100644
--- a/paging/paging-testing/build.gradle
+++ b/paging/paging-testing/build.gradle
@@ -29,7 +29,6 @@
 
 plugins {
     id("AndroidXPlugin")
-    id("com.android.library")
 }
 
 androidXMultiplatform {
@@ -39,7 +38,14 @@
     ios()
     watchos()
     tvos()
-    android()
+    androidLibrary {
+        namespace = "androidx.paging.testing"
+        withAndroidTestOnDeviceBuilder {
+            it.compilationName = "instrumentedTest"
+            it.defaultSourceSetName = "androidInstrumentedTest"
+            it.sourceSetTreeName = "test"
+        }
+    }
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -131,6 +137,3 @@
     metalavaK2UastEnabled = false
 }
 
-android {
-    namespace "androidx.paging.testing"
-}
diff --git a/pdf/pdf-viewer/src/main/res/values-af/strings.xml b/pdf/pdf-viewer/src/main/res/values-af/strings.xml
index 269c461..3efa5d2 100644
--- a/pdf/pdf-viewer/src/main/res/values-af/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-af/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Kon nie die lêer oopmaak nie. Moontlike toestemmingkwessie?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Bladsy is vir die PDF-dokument gebreek"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Onvoldoende data om die PDF-dokument te verwerk"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-am/strings.xml b/pdf/pdf-viewer/src/main/res/values-am/strings.xml
index 517f867..a2679692 100644
--- a/pdf/pdf-viewer/src/main/res/values-am/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-am/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ፋይሉን መክፈት አልተሳካም። የፈቃድ ችግር ሊሆን ይችላል?"</string>
     <string name="page_broken" msgid="2968770793669433462">"ለPDF ሰነዱ ገፅ ተበላሽቷል"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF ሰነዱን ለማሰናዳት በቂ ያልሆነ ውሂብ"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ar/strings.xml b/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
index 3b33c01..eae5948 100644
--- a/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"تعذّر فتح الملف. هل توجد مشكلة محتملة في الأذونات؟"</string>
     <string name="page_broken" msgid="2968770793669433462">"‏تعذّر تحميل صفحة من مستند PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"‏البيانات غير كافية لمعالجة مستند PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-as/strings.xml b/pdf/pdf-viewer/src/main/res/values-as/strings.xml
index 6aff8a9eb..f9c9ef7 100644
--- a/pdf/pdf-viewer/src/main/res/values-as/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-as/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ফাইলটো খুলিব পৰা নগ’ল। সম্ভাব্য অনুমতি সম্পৰ্কীয় সমস্যা?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF নথিৰ বাবে পৃষ্ঠাখন বিসংগতিপূৰ্ণ"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF নথিখন প্ৰক্ৰিয়াকৰণ কৰিবলৈ অপৰ্যাপ্ত ডেটা"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-az/strings.xml b/pdf/pdf-viewer/src/main/res/values-az/strings.xml
index 622b95f..6ef1751 100644
--- a/pdf/pdf-viewer/src/main/res/values-az/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-az/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Fayl açılmadı. İcazə problemi var?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF sənədi üçün səhifədə xəta var"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF sənədini emal etmək üçün kifayət qədər data yoxdur"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml b/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
index 0495044..f259654 100644
--- a/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Otvaranje fajla nije uspelo. Možda postoje problemi sa dozvolom?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Neispravna stranica za PDF dokument"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Nedovoljno podataka za obradu PDF dokumenta"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-be/strings.xml b/pdf/pdf-viewer/src/main/res/values-be/strings.xml
index 6d25879..c951b2c 100644
--- a/pdf/pdf-viewer/src/main/res/values-be/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-be/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Не ўдалося адкрыць файл. Магчыма, праблема з дазволам?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Старонка дакумента PDF пашкоджана"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Не хапае даных для апрацоўкі дакумента PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-bg/strings.xml b/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
index 37f6840..004be66 100644
--- a/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"Файлът не бе отворен. Възможно е да има проблем с разрешенията."</string>
     <string name="page_broken" msgid="2968770793669433462">"Невалидна страница в PDF документа"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Няма достатъчно данни за обработването на PDF документа"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF файлът не може да се отвори"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-bn/strings.xml b/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
index a65a18c..937be09 100644
--- a/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ফাইল খোলা যায়নি। অনুমতি সংক্রান্ত সমস্যার কারণে এটি হতে পারে?"</string>
     <string name="page_broken" msgid="2968770793669433462">"পিডিএফ ডকুমেন্টের ক্ষেত্রে পৃষ্ঠা ভেঙে গেছে"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"পিডিএফ ডকুমেন্ট প্রসেস করার জন্য যথেষ্ট ডেটা নেই"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-bs/strings.xml b/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
index 642b643..cd49b7a 100644
--- a/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Otvaranje fajla nije uspjelo. Možda postoji problem s odobrenjem?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Stranica je prelomljena za PDF dokument"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Nema dovoljno podataka za obradu PDF dokumenta"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ca/strings.xml b/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
index 9338452..4f3deac 100644
--- a/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"No s\'ha pogut obrir el fitxer. És possible que hi hagi un problema de permisos?"</string>
     <string name="page_broken" msgid="2968770793669433462">"La pàgina no funciona per al document PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Les dades són insuficients per processar el document PDF"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"No es pot obrir el fitxer PDF"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-cs/strings.xml b/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
index 07d4e4e..cdc4b65 100644
--- a/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Soubor se nepodařilo otevřít. Může se jednat o problém s oprávněním."</string>
     <string name="page_broken" msgid="2968770793669433462">"Dokument PDF obsahuje poškozenou stránku"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Nedostatek dat ke zpracování dokumentu PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-da/strings.xml b/pdf/pdf-viewer/src/main/res/values-da/strings.xml
index 9ad79da..5bcd02c 100644
--- a/pdf/pdf-viewer/src/main/res/values-da/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-da/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Filen kunne ikke åbnes Mon der er et problem med tilladelserne?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Siden er ødelagt for PDF-dokumentet"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Der er ikke nok data til at behandle PDF-dokumentet"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-de/strings.xml b/pdf/pdf-viewer/src/main/res/values-de/strings.xml
index 45bac2a..72b1096 100644
--- a/pdf/pdf-viewer/src/main/res/values-de/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-de/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Datei konnte nicht geöffnet werden. Möglicherweise ein Berechtigungsproblem?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Seite für PDF-Dokument ist fehlerhaft"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Keine ausreichenden Daten, um das PDF-Dokument zu verarbeiten"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-el/strings.xml b/pdf/pdf-viewer/src/main/res/values-el/strings.xml
index e87a5b8..ada33d8 100644
--- a/pdf/pdf-viewer/src/main/res/values-el/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-el/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Δεν ήταν δυνατό το άνοιγμα του αρχείου. Μήπως υπάρχει κάποιο πρόβλημα με την άδεια;"</string>
     <string name="page_broken" msgid="2968770793669433462">"Δεν ήταν δυνατή η φόρτωση του εγγράφου PDF από τη σελίδα"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Μη επαρκή δεδομένα για την επεξεργασία του εγγράφου PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
index 62dcae9..2f9e726 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Failed to open the file. Possible permission issue?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml
index 5a09605..66f8a08 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"Failed to open the file. Possible permission issue?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"Can\'t open PDF file"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
index 62dcae9..2f9e726 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Failed to open the file. Possible permission issue?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
index 62dcae9..2f9e726 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Failed to open the file. Possible permission issue?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rXC/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rXC/strings.xml
index d2bf4b8..f0cfb06 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rXC/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rXC/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‎Failed to open the file. Possible permission issue?‎‏‎‎‏‎"</string>
     <string name="page_broken" msgid="2968770793669433462">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‎‎Page broken for the PDF document‎‏‎‎‏‎"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎Insufficient data for processing the PDF document‎‏‎‎‏‎"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‏‏‏‎‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‎‏‏‏‏‎Can\'t open PDF file‎‏‎‎‏‎"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml b/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
index 4ad89b2..388c071 100644
--- a/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
@@ -42,12 +42,9 @@
     <string name="desc_page_range" msgid="5286496438609641577">"páginas <xliff:g id="FIRST">%1$d</xliff:g> a <xliff:g id="LAST">%2$d</xliff:g> de <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
     <string name="desc_image_alt_text" msgid="7700601988820586333">"Imagen: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
     <string name="hint_find" msgid="5385388836603550565">"Buscar en el archivo"</string>
-    <!-- no translation found for previous_button_description (1169511027880317546) -->
-    <skip />
-    <!-- no translation found for next_button_description (4702699322249103693) -->
-    <skip />
-    <!-- no translation found for close_button_description (7379823906921067675) -->
-    <skip />
+    <string name="previous_button_description" msgid="1169511027880317546">"Anterior"</string>
+    <string name="next_button_description" msgid="4702699322249103693">"Siguiente"</string>
+    <string name="close_button_description" msgid="7379823906921067675">"Cerrar"</string>
     <string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_edit" msgid="5882082700509010966">"Editar el archivo"</string>
     <string name="password_not_entered" msgid="8875370870743585303">"Ingresa la contraseña para desbloquear"</string>
@@ -56,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"No se pudo abrir el archivo. ¿Puede que se deba a un problema de permisos?"</string>
     <string name="page_broken" msgid="2968770793669433462">"La página no funciona para el documento PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"No hay datos suficientes para procesar el documento PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-es/strings.xml b/pdf/pdf-viewer/src/main/res/values-es/strings.xml
index d08f531..b1aca5f 100644
--- a/pdf/pdf-viewer/src/main/res/values-es/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-es/strings.xml
@@ -42,12 +42,9 @@
     <string name="desc_page_range" msgid="5286496438609641577">"páginas <xliff:g id="FIRST">%1$d</xliff:g> a <xliff:g id="LAST">%2$d</xliff:g> de <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
     <string name="desc_image_alt_text" msgid="7700601988820586333">"Imagen: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
     <string name="hint_find" msgid="5385388836603550565">"Buscar en el archivo"</string>
-    <!-- no translation found for previous_button_description (1169511027880317546) -->
-    <skip />
-    <!-- no translation found for next_button_description (4702699322249103693) -->
-    <skip />
-    <!-- no translation found for close_button_description (7379823906921067675) -->
-    <skip />
+    <string name="previous_button_description" msgid="1169511027880317546">"Anterior"</string>
+    <string name="next_button_description" msgid="4702699322249103693">"Siguiente"</string>
+    <string name="close_button_description" msgid="7379823906921067675">"Cerrar"</string>
     <string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_edit" msgid="5882082700509010966">"Editar archivo"</string>
     <string name="password_not_entered" msgid="8875370870743585303">"Introduce la contraseña para desbloquear"</string>
@@ -56,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"No se ha podido abrir el archivo. ¿Puede que se deba a un problema de permisos?"</string>
     <string name="page_broken" msgid="2968770793669433462">"La página no funciona para el documento PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Datos insuficientes para procesar el documento PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-et/strings.xml b/pdf/pdf-viewer/src/main/res/values-et/strings.xml
index 902cdd0..d640ef5 100644
--- a/pdf/pdf-viewer/src/main/res/values-et/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-et/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Faili avamine nurjus. Probleem võib olla seotud lubadega."</string>
     <string name="page_broken" msgid="2968770793669433462">"Rikutud leht PDF-dokumendis"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF-dokumendi töötlemiseks pole piisavalt andmeid"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-eu/strings.xml b/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
index 239b72c..20770aa 100644
--- a/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Ezin izan da ireki fitxategia. Agian ez duzu horretarako baimenik?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF dokumentuaren orria hondatuta dago"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Ez dago behar adina daturik PDF dokumentua prozesatzeko"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fa/strings.xml b/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
index 42aa1cd..0669b17 100644
--- a/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"فایل باز نشد. احتمالاً مشکلی در اجازه وجود دارد؟"</string>
     <string name="page_broken" msgid="2968770793669433462">"‏صفحه سند PDF خراب است"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"‏داده‌ها برای پردازش سند PDF کافی نیست"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fi/strings.xml b/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
index cdce79c..3518c19 100644
--- a/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Tiedoston avaaminen epäonnistui. Mahdollinen lupaan liittyvä ongelma?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF-dokumenttiin liittyvä sivu on rikki"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Riittämätön data PDF-dokumentin käsittelyyn"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml b/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
index 453aacf..8a25992 100644
--- a/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Échec de l\'ouverture du fichier. Problème d\'autorisation éventuel?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Page brisée pour le document PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Données insuffisantes pour le traitement du document PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fr/strings.xml b/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
index cffc34d..dd5fcad 100644
--- a/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Échec de l\'ouverture du fichier. Problème d\'autorisation possible ?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Page non fonctionnelle pour le document PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Données insuffisantes pour le traitement du document PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-gl/strings.xml b/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
index 67960e4..ebe0816 100644
--- a/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Produciuse un erro ao abrir o ficheiro. É posible que haxa problemas co permiso?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Non funciona a páxina para o documento PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Os datos non son suficientes para procesar o documento PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-gu/strings.xml b/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
index f0fe5ae..ae95929 100644
--- a/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ફાઇલ ખોલવામાં નિષ્ફળ રહ્યાં. શું તમારી પાસે આની પરવાનગી નથી?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF દસ્તાવેજ માટે પેજ લોડ થઈ રહ્યું નથી"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF દસ્તાવેજ પર પ્રક્રિયા કરવા માટે પર્યાપ્ત ડેટા નથી"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-hi/strings.xml b/pdf/pdf-viewer/src/main/res/values-hi/strings.xml
index d1fe782..49f8c1d 100644
--- a/pdf/pdf-viewer/src/main/res/values-hi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hi/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"फ़ाइल नहीं खोली जा सकी. क्या आपके पास इसकी अनुमति नहीं है?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF दस्तावेज़ के लिए पेज लोड नहीं हो रहा है"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF दस्तावेज़ को प्रोसेस करने के लिए, ज़रूरत के मुताबिक डेटा नहीं है"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF फ़ाइल नहीं खोली जा सकी"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-hr/strings.xml b/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
index 4605373..7248c6ee 100644
--- a/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Otvaranje datoteke nije uspjelo. Možda postoji problem s dopuštenjem?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Stranica je raščlanjena za PDF dokument"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Nema dovoljno podataka za obradu PDF dokumenta"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-hu/strings.xml b/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
index a733547..f63a921 100644
--- a/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Nem sikerült megnyitni a fájlt. Engedéllyel kapcsolatos problémáról lehet szó?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Az oldal nem tölt be a PDF-dokumentumban"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Nem áll rendelkezésre elegendő adat a PDF-dokumentum feldolgozásához"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-hy/strings.xml b/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
index 901715c..71d24c2 100644
--- a/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Չհաջողվեց բացել ֆայլը։ Գուցե թույլտվության հետ կապված խնդի՞ր է։"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF փաստաթղթի էջը վնասված է"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Ոչ բավարար տվյալներ PDF փաստաթղթի մշակման համար"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-in/strings.xml b/pdf/pdf-viewer/src/main/res/values-in/strings.xml
index fa9c752..3c9e624 100644
--- a/pdf/pdf-viewer/src/main/res/values-in/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-in/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Gagal membuka file. Kemungkinan masalah izin?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Halaman dokumen PDF rusak"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Data tidak cukup untuk memproses dokumen PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-is/strings.xml b/pdf/pdf-viewer/src/main/res/values-is/strings.xml
index 9716ab1..9378e05 100644
--- a/pdf/pdf-viewer/src/main/res/values-is/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-is/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Ekki tókst að opna skrána. Hugsanlega vandamál tengt heimildum?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Síða í PDF-skjali er gölluð"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Ekki næg gögn fyrir úrvinnslu á PDF-skjali"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-it/strings.xml b/pdf/pdf-viewer/src/main/res/values-it/strings.xml
index 564eecd..89a8d17 100644
--- a/pdf/pdf-viewer/src/main/res/values-it/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-it/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Impossibile aprire il file. Possibile problema di autorizzazione?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Pagina inaccessibile per il documento PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Dati insufficienti per l\'elaborazione del documento PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-iw/strings.xml b/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
index 6ce9431..3dfd510 100644
--- a/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"לא ניתן לפתוח את הקובץ. יכול להיות שיש בעיה בהרשאה."</string>
     <string name="page_broken" msgid="2968770793669433462">"‏קישור מנותק בדף למסמך ה-PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"‏אין מספיק נתונים כדי לעבד את מסמך ה-PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ja/strings.xml b/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
index f03d62a..a48cc46 100644
--- a/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"ファイルを開けませんでした。権限に問題がある可能性はありませんか?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF ドキュメントのページが壊れています"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"データ不足のため PDF ドキュメントを処理できません"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ファイルを開けません"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ka/strings.xml b/pdf/pdf-viewer/src/main/res/values-ka/strings.xml
index 864fd74..3fd5c9b 100644
--- a/pdf/pdf-viewer/src/main/res/values-ka/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ka/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"ფაილის გახსნა ვერ მოხერხდა. შესაძლოა ნებართვის პრობლემა იყოს?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF დოკუმენტის გვერდი დაზიანებულია"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"მონაცემები არ არის საკმარისი PDF დოკუმენტის დასამუშავებლად"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ფაილის გახსნა ვერ ხერხდება"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-kk/strings.xml b/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
index b94ea34..5846202 100644
--- a/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Файл ашылмады. Бәлкім, рұқсатқа қатысты бір мәселе бар?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF құжатының беті бұзылған."</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF құжатын өңдеу үшін деректер жеткіліксіз."</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-km/strings.xml b/pdf/pdf-viewer/src/main/res/values-km/strings.xml
index 03a0ebd..931c43f 100644
--- a/pdf/pdf-viewer/src/main/res/values-km/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-km/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"មិនអាច​បើក​ឯកសារនេះ​បានទេ។ អាចមាន​បញ្ហា​នៃ​ការអនុញ្ញាតឬ?"</string>
     <string name="page_broken" msgid="2968770793669433462">"ទំព័រ​មិនដំណើរការ​សម្រាប់​ឯកសារ PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"មានទិន្នន័យ​មិនគ្រប់គ្រាន់​សម្រាប់​ដំណើរការ​ឯកសារ PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-kn/strings.xml b/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
index 4c114f7..fb8b009 100644
--- a/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ಫೈಲ್ ತೆರೆಯಲು ವಿಫಲವಾಗಿದೆ. ಸಂಭವನೀಯ ಅನುಮತಿ ಸಮಸ್ಯೆ?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF ಡಾಕ್ಯುಮೆಂಟ್‌ಗೆ ಸಂಬಂಧಿಸಿದ ಪುಟ ಮುರಿದಿದೆ"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF ಡಾಕ್ಯುಮೆಂಟ್ ಅನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಕಷ್ಟು ಡೇಟಾ ಇಲ್ಲ"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ko/strings.xml b/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
index 54655ec..9530691 100644
--- a/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"파일을 열 수 없습니다. 권한 문제가 있을 수 있나요?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF 문서의 페이지가 손상되었습니다."</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF 문서 처리를 위한 데이터가 부족합니다."</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ky/strings.xml b/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
index 90e6450..92e5119 100644
--- a/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Файл ачылган жок. Керектүү уруксаттар жок окшойт."</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF документинин барагы бузук"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF документин иштетүү үчүн маалымат жетишсиз"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-lo/strings.xml b/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
index 895280e..baa1c28 100644
--- a/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ເປີດໄຟລ໌ບໍ່ສຳເລັດ. ອາດເປັນຍ້ອນບັນຫາທາງການອະນຸຍາດບໍ?"</string>
     <string name="page_broken" msgid="2968770793669433462">"ໜ້າເສຍຫາຍສໍາລັບເອກະສານ PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"ຂໍ້ມູນບໍ່ພຽງພໍສໍາລັບການປະມວນຜົນເອກະສານ PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-lt/strings.xml b/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
index 8b14e98..588ff87 100644
--- a/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Nepavyko atidaryti failo. Galima su leidimais susijusi problema?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Sugadintas PDF dokumento puslapis"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Nepakanka duomenų PDF dokumentui apdoroti"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-lv/strings.xml b/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
index 22e20c9..738615d 100644
--- a/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Neizdevās atvērt failu. Iespējams, ir radusies problēma ar atļaujām."</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF dokumenta lapa ir bojāta"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Nepietiekams datu apjoms, lai apstrādātu PDF dokumentu"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-mk/strings.xml b/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
index cce9ac3..e6b85c2 100644
--- a/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Не можеше да се отвори датотеката. Можеби има проблем со дозволата?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Страницата не може да го вчита PDF-документот"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Недоволно податоци за обработка на PDF-документот"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ml/strings.xml b/pdf/pdf-viewer/src/main/res/values-ml/strings.xml
index b07acaa..760a696 100644
--- a/pdf/pdf-viewer/src/main/res/values-ml/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ml/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ഫയൽ തുറക്കാനായില്ല. അനുമതി സംബന്ധിച്ച പ്രശ്‌നമാകാൻ സാധ്യതയുണ്ടോ?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF ഡോക്യുമെന്റിനായി പേജ് ലോഡ് ചെയ്യാനായില്ല"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF ഡോക്യുമെന്റ് പ്രോസസ് ചെയ്യാൻ മതിയായ ഡാറ്റയില്ല"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-mn/strings.xml b/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
index 756390b..9c8f052 100644
--- a/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Файлыг нээж чадсангүй. Зөвшөөрөлтэй холбоотой асуудал байж болох уу?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF баримт бичгийн хуудас эвдэрсэн"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF баримт бичгийг боловсруулахад өгөгдөл хангалтгүй байна"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-mr/strings.xml b/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
index a6d1c6e..40ee1a8 100644
--- a/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"फाइल उघडता आली नाही. परवानगीशी संबंधित संभाव्य समस्या?"</string>
     <string name="page_broken" msgid="2968770793669433462">"पीडीएफ दस्तऐवजासाठी पेज खंडित झाले आहे"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF दस्तऐवजावर प्रक्रिया करण्यासाठी डेटा पुरेसा नाही"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ms/strings.xml b/pdf/pdf-viewer/src/main/res/values-ms/strings.xml
index acf0ee8..f905244 100644
--- a/pdf/pdf-viewer/src/main/res/values-ms/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ms/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Gagal membuka fail. Kemungkinan terdapat masalah berkaitan dengan kebenaran?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Halaman rosak untuk dokumen PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Data tidak mencukupi untuk memproses dokumen PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-my/strings.xml b/pdf/pdf-viewer/src/main/res/values-my/strings.xml
index bd311a7..c288df3 100644
--- a/pdf/pdf-viewer/src/main/res/values-my/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-my/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ဖိုင်ကို ဖွင့်၍မရလိုက်ပါ။ ခွင့်ပြုချက် ပြဿနာ ဖြစ်နိုင်လား။"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF မှတ်တမ်းအတွက် စာမျက်နှာ ပျက်နေသည်"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF မှတ်တမ်း လုပ်ဆောင်ရန်အတွက် ဒေတာ မလုံလောက်ပါ"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-nb/strings.xml b/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
index 23107ac..06e5547 100644
--- a/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Kunne ikke åpne filen. Kan det være et problem med tillatelser?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Siden er ødelagt for PDF-dokumentet"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Det er utilstrekkelige data for behandling av PDF-dokumentet"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ne/strings.xml b/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
index da53207..d847ed0c 100644
--- a/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"फाइल खोल्न सकिएन। तपाईंसँग यो फाइल खोल्ने अनुमति छैन?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF डकुमेन्टको पेज लोड गर्न सकिएन"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF डकुमेन्ट प्रोसेस गर्न पर्याप्त जानकारी छैन"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-nl/strings.xml b/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
index 9ad4b40..5ceea0c 100644
--- a/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Kan het bestand niet openen. Mogelijk rechtenprobleem?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Pagina van het pdf-document kan niet worden geladen"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Onvoldoende gegevens om het pdf-document te verwerken"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-or/strings.xml b/pdf/pdf-viewer/src/main/res/values-or/strings.xml
index cf3e282..66b7f68 100644
--- a/pdf/pdf-viewer/src/main/res/values-or/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-or/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ଫାଇଲ ଖୋଲିବାରେ ବିଫଳ ହୋଇଛି। ସମ୍ଭାବ୍ୟ ଅନୁମତି ସମସ୍ୟା ଅଛି?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF ଡକ୍ୟୁମେଣ୍ଟ ପାଇଁ ପୃଷ୍ଠା ବିଭାଜିତ ହୋଇଛି"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF ଡକ୍ୟୁମେଣ୍ଟ ପ୍ରକ୍ରିୟାକରଣ ପାଇଁ ପର୍ଯ୍ୟାପ୍ତ ଡାଟା ନାହିଁ"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pa/strings.xml b/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
index 86a73eb..8d4c105 100644
--- a/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ਫ਼ਾਈਲ ਨੂੰ ਖੋਲ੍ਹਣਾ ਅਸਫਲ ਰਿਹਾ। ਕੀ ਸੰਭਵ ਇਜਾਜ਼ਤ ਸੰਬੰਧੀ ਸਮੱਸਿਆ ਹੈ?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF ਦਸਤਾਵੇਜ਼ ਲਈ ਪੰਨਾ ਲੋਡ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF ਦਸਤਾਵੇਜ਼ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਕਰਨ ਲਈ ਲੋੜੀਂਦਾ ਡਾਟਾ ਨਹੀਂ ਹੈ"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pl/strings.xml b/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
index ce974f6..6583cb5 100644
--- a/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Nie udało się otworzyć pliku. Może to przez problem z uprawnieniami?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Strona w dokumencie PDF jest uszkodzona"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Brak wystarczającej ilości danych do przetworzenia dokumentu PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
index f3fdf6c..e8beb95 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Falha ao abrir o arquivo. Possível problema de permissão?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Página do documento PDF corrompida"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Dados insuficientes para processamento do documento PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
index ff10bc1..2a1a80a 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"Falha ao abrir o ficheiro. Possível problema de autorização?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Página danificada para o documento PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Dados insuficientes para processar o documento PDF"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"Não é possível abrir o ficheiro PDF"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
index f3fdf6c..e8beb95 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Falha ao abrir o arquivo. Possível problema de permissão?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Página do documento PDF corrompida"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Dados insuficientes para processamento do documento PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ro/strings.xml b/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
index 669086d..ef6b73d 100644
--- a/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Nu s-a putut deschide fișierul. Există vreo problemă cu permisiunile?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Pagină deteriorată pentru documentul PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Date insuficiente pentru procesarea documentului PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ru/strings.xml b/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
index d7163f2..665e7cb 100644
--- a/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Не удалось открыть файл. Возможно, нет необходимых разрешений."</string>
     <string name="page_broken" msgid="2968770793669433462">"Страница документа PDF повреждена"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Недостаточно данных для обработки документа PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-si/strings.xml b/pdf/pdf-viewer/src/main/res/values-si/strings.xml
index bba16c5..dc5d075 100644
--- a/pdf/pdf-viewer/src/main/res/values-si/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-si/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ගොනුව විවෘත කිරීමට අසමත් විය. අවසර ගැටලුවක් විය හැකි ද?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF ලේඛනය සඳහා පිටුව හානි වී ඇත"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF ලේඛනය සැකසීම සඳහා ප්‍රමාණවත් දත්ත නොමැත"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sk/strings.xml b/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
index f8f3997..d44c7b6 100644
--- a/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Súbor sa nepodarilo otvoriť. Možno sa vyskytol problém s povolením."</string>
     <string name="page_broken" msgid="2968770793669433462">"Stránka sa v dokumente vo formáte PDF nedá načítať"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"V dokumente vo formáte PDF nie je dostatok údajov na spracovanie"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sl/strings.xml b/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
index ad7b4cd..afef99a 100644
--- a/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"Odpiranje datoteke ni uspelo. Ali morda gre za težavo z dovoljenjem?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Strani iz dokumenta PDF ni mogoče prikazati"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Nezadostni podatki za obdelavo dokumenta PDF"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"Datoteke PDF ni mogoče odpreti"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sq/strings.xml b/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
index 0e775ba..37347d3 100644
--- a/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Hapja e skedarit dështoi. Problem i mundshëm me lejet?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Faqe e dëmtuar për dokumentin PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Të dhëna të pamjaftueshme për përpunimin e dokumentit PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sr/strings.xml b/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
index d70749c..bcab510 100644
--- a/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Отварање фајла није успело. Можда постоје проблеми са дозволом?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Неисправна страница за PDF документ"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Недовољно података за обраду PDF документа"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sv/strings.xml b/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
index eae0c6a..3fa349f 100644
--- a/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Det gick inte att öppna filen. Detta kan bero på ett behörighetsproblem."</string>
     <string name="page_broken" msgid="2968770793669433462">"Det gick inte att läsa in en sida i PDF-dokumentet"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Otillräcklig data för att behandla PDF-dokumentet"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sw/strings.xml b/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
index e976caf..abf9c4b5 100644
--- a/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Imeshindwa kufungua faili. Je, linaweza kuwa tatizo la ruhusa?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Ukurasa wa hati ya PDF una tatizo"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Hamna data ya kutosha kuchakata hati ya PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ta/strings.xml b/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
index 8a51fa4..2d1d291 100644
--- a/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"ஃபைலைத் திறக்க முடியவில்லை. அனுமதி தொடர்பான சிக்கல் உள்ளதா?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF ஆவணத்தை ஏற்ற முடியவில்லை"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF ஆவணத்தைச் செயலாக்குவதற்குப் போதுமான தரவு இல்லை"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-te/strings.xml b/pdf/pdf-viewer/src/main/res/values-te/strings.xml
index e5ff775..ffcee7a 100644
--- a/pdf/pdf-viewer/src/main/res/values-te/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-te/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"ఫైల్‌ను తెరవడం విఫలమైంది. అనుమతికి సంబంధించిన సమస్య కావచ్చా?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF డాక్యుమెంట్‌కు సంబంధించి పేజీ బ్రేక్ అయింది"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF డాక్యుమెంట్‌ను ప్రాసెస్ చేయడానికి డేటా తగినంత లేదు"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ఫైల్‌ను తెరవడం సాధ్యపడదు"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-th/strings.xml b/pdf/pdf-viewer/src/main/res/values-th/strings.xml
index 971e3e2..be31750 100644
--- a/pdf/pdf-viewer/src/main/res/values-th/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-th/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"เปิดไฟล์ไม่สำเร็จ อาจเกิดจากปัญหาด้านสิทธิ์"</string>
     <string name="page_broken" msgid="2968770793669433462">"หน้าในเอกสาร PDF เสียหาย"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"ข้อมูลไม่เพียงพอสำหรับการประมวลผลเอกสาร PDF"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"เปิดไฟล์ PDF ไม่ได้"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-tl/strings.xml b/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
index be6d695..5b20647 100644
--- a/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"Hindi nabuksan ang file. Baka may isyu sa pahintulot?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Sira ang page para sa PDF na dokumento"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Kulang ang data para maproseso ang PDF na dokumento"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"Hindi mabuksan ang PDF file"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-tr/strings.xml b/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
index 09803ed..8ad6bf2 100644
--- a/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Dosya açılamadı. İzin sorunundan kaynaklanıyor olabilir mi?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF dokümanının sayfası bozuk"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF dokümanını işleyecek kadar yeterli veri yok"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-uk/strings.xml b/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
index fc81a4b..c381f20 100644
--- a/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Не вдалося відкрити файл. Можливо, виникла проблема з дозволом."</string>
     <string name="page_broken" msgid="2968770793669433462">"Сторінку документа PDF пошкоджено"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Недостатньо даних для обробки документа PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ur/strings.xml b/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
index e733c19..6027e36 100644
--- a/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"فائل کھولنے میں ناکام۔ کیا یہ اجازت کا مسئلہ ہو سکتا ہے؟"</string>
     <string name="page_broken" msgid="2968770793669433462">"‏‫PDF دستاویز کیلئے شکستہ صفحہ"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"‏‫PDF دستاویز پر کارروائی کرنے کیلئے ڈیٹا ناکافی ہے"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-uz/strings.xml b/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
index 54c3148..41b89f8 100644
--- a/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"Fayl ochilmadi. Ruxsat bilan muammo bormi?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF hujjat sahifasi yaroqsiz"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"PDF hujjatni qayta ishlash uchun kerakli axborotlar yetarli emas"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF fayk ochilmadi"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-vi/strings.xml b/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
index c2015bc..5f50634 100644
--- a/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Không mở được tệp này. Có thể là do vấn đề về quyền?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Tài liệu PDF này bị lỗi trang"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Không đủ dữ liệu để xử lý tài liệu PDF này"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
index 4abd99c..c6dbf7e 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
@@ -53,4 +53,5 @@
     <string name="file_error" msgid="4003885928556884091">"无法打开文件。可能是由于权限问题导致?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF 文档的页面已损坏"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"数据不足,无法处理 PDF 文档"</string>
+    <string name="error_cannot_open_pdf" msgid="2361919778558145071">"无法打开 PDF 文件"</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
index 3510530..691bdfe 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"無法開啟檔案。可能有權限問題?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF 文件頁面已損毀"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"沒有足夠資料處理 PDF 文件"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
index 10fd054..b2be3f4 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"無法開啟檔案。有可能是權限問題?"</string>
     <string name="page_broken" msgid="2968770793669433462">"PDF 文件的頁面損毀"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"資料不足,無法處理 PDF 文件"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zu/strings.xml b/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
index 4065bd2..ba456e5 100644
--- a/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
@@ -53,4 +53,6 @@
     <string name="file_error" msgid="4003885928556884091">"Yehlulekile ukuvula ifayela. Inkinga yemvume engaba khona?"</string>
     <string name="page_broken" msgid="2968770793669433462">"Ikhasi eliphuliwe ledokhumenti ye-PDF"</string>
     <string name="needs_more_data" msgid="3520133467908240802">"Idatha enganele yokucubungula idokhumenti ye-PDF"</string>
+    <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+    <skip />
 </resources>
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
index bb5cec2..fb5e7c0 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
+++ b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
@@ -75,7 +75,7 @@
 }
 
 androidComponents {
-    onVariants(selector().withBuildType("debug")) {
+    onVariants(selector().withBuildType("release")) {
         androidTest.sources.assets.addGeneratedSourceDirectory(
                 bundleTestSdkDexTaskProvider,
                 BundleTestSdkDexTask::getOutputDir
diff --git a/profileinstaller/integration-tests/profile-verification/build.gradle b/profileinstaller/integration-tests/profile-verification/build.gradle
index cf670e9..416e4a0 100644
--- a/profileinstaller/integration-tests/profile-verification/build.gradle
+++ b/profileinstaller/integration-tests/profile-verification/build.gradle
@@ -122,5 +122,5 @@
 
 // It makes sure that the apks are generated before the assets are packed.
 afterEvaluate {
-    tasks.named("generateDebugAndroidTestAssets").configure { it.dependsOn(prepareAssetsTaskProvider) }
+    tasks.named("generateReleaseAndroidTestAssets").configure { it.dependsOn(prepareAssetsTaskProvider) }
 }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
index 7c21438..a1cddb7 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
@@ -24,6 +24,7 @@
 import androidx.room.compiler.processing.util.getParameter
 import androidx.room.compiler.processing.util.runKaptTest
 import androidx.room.compiler.processing.util.runProcessorTest
+import org.junit.Ignore
 import org.junit.Test
 
 class KotlinMetadataTest {
@@ -48,6 +49,7 @@
         }
     }
 
+    @Ignore("b/360398921")
     @Test
     fun inlineReifiedFunctionAndKAPT4() {
         val source =
diff --git a/room/room-migration/bcv/native/current.txt b/room/room-migration/bcv/native/current.txt
index 0b1314c..9b3bd15 100644
--- a/room/room-migration/bcv/native/current.txt
+++ b/room/room-migration/bcv/native/current.txt
@@ -337,9 +337,6 @@
 }
 
 sealed class androidx.room.migration.bundle/BaseEntityBundle { // androidx.room.migration.bundle/BaseEntityBundle|null[0]
-    constructor <init>() // androidx.room.migration.bundle/BaseEntityBundle.<init>|<init>(){}[0]
-    constructor <init>(kotlin/Int, kotlinx.serialization.internal/SerializationConstructorMarker?) // androidx.room.migration.bundle/BaseEntityBundle.<init>|<init>(kotlin.Int;kotlinx.serialization.internal.SerializationConstructorMarker?){}[0]
-
     abstract val createSql // androidx.room.migration.bundle/BaseEntityBundle.createSql|{}createSql[0]
         abstract fun <get-createSql>(): kotlin/String // androidx.room.migration.bundle/BaseEntityBundle.createSql.<get-createSql>|<get-createSql>(){}[0]
     abstract val fields // androidx.room.migration.bundle/BaseEntityBundle.fields|{}fields[0]
diff --git a/settings.gradle b/settings.gradle
index d0f669c..ded0605 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -419,6 +419,8 @@
 includeProject(":camera:camera-camera2-pipe", [BuildType.CAMERA])
 includeProject(":camera:camera-camera2-pipe-integration", [BuildType.CAMERA])
 includeProject(":camera:camera-camera2-pipe-testing", [BuildType.CAMERA])
+includeProject(":camera:camera-compose", [BuildType.CAMERA])
+includeProject(":camera:camera-compose:camera-compose-samples", "camera/camera-compose/samples", [BuildType.CAMERA])
 includeProject(":camera:camera-core", [BuildType.CAMERA])
 includeProject(":camera:camera-effects", [BuildType.CAMERA])
 includeProject(":camera:camera-effects-still-portrait", [BuildType.CAMERA])
@@ -597,6 +599,7 @@
 includeProject(":contentpager:contentpager", [BuildType.MAIN])
 includeProject(":coordinatorlayout:coordinatorlayout", [BuildType.MAIN])
 includeProject(":core:core", [BuildType.MAIN, BuildType.GLANCE, BuildType.MEDIA, BuildType.FLAN, BuildType.COMPOSE])
+includeProject(":core:core:core-samples", "core/core/samples", [BuildType.MAIN])
 includeProject(":core:core-testing", [BuildType.MAIN, BuildType.GLANCE, BuildType.MEDIA, BuildType.FLAN, BuildType.COMPOSE])
 includeProject(":core:core:integration-tests:publishing", [BuildType.MAIN])
 includeProject(":core:core-animation", [BuildType.MAIN])
@@ -655,6 +658,7 @@
 includeProject(":datastore:datastore-rxjava2", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
 includeProject(":datastore:datastore-rxjava3", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
 includeProject(":datastore:datastore-sampleapp", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:integration-tests:testapp", [BuildType.MAIN])
 includeProject(":documentfile:documentfile", [BuildType.MAIN])
 includeProject(":draganddrop:draganddrop", [BuildType.MAIN])
 includeProject(":draganddrop:integration-tests:sampleapp", [BuildType.MAIN])
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
index 1d4f54a..62969ec 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
@@ -140,7 +140,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressMenu();
-        assertTrue(textView.wait(Until.textEquals("keycode menu pressed"), TIMEOUT_MS));
+        assertTrue(textView.wait(Until.textEquals("keycode menu pressed; "), TIMEOUT_MS));
     }
 
     @Test
@@ -149,7 +149,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressBack();
-        assertTrue(textView.wait(Until.textEquals("keycode back pressed"), TIMEOUT_MS));
+        assertTrue(textView.wait(Until.textEquals("keycode back pressed; "), TIMEOUT_MS));
     }
 
     @Test
@@ -158,7 +158,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressSearch();
-        assertTrue(textView.wait(Until.textEquals("keycode search pressed"), TIMEOUT_MS));
+        assertTrue(textView.wait(Until.textEquals("keycode search pressed; "), TIMEOUT_MS));
     }
 
     @Test
@@ -167,7 +167,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressDPadCenter();
-        assertTrue(textView.wait(Until.textEquals("keycode dpad center pressed"), TIMEOUT_MS));
+        assertTrue(textView.wait(Until.textEquals("keycode dpad center pressed; "), TIMEOUT_MS));
     }
 
     @Test
@@ -176,7 +176,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressDPadDown();
-        assertTrue(textView.wait(Until.textEquals("keycode dpad down pressed"), TIMEOUT_MS));
+        assertTrue(textView.wait(Until.textEquals("keycode dpad down pressed; "), TIMEOUT_MS));
     }
 
     @Test
@@ -185,7 +185,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressDPadUp();
-        assertTrue(textView.wait(Until.textEquals("keycode dpad up pressed"), TIMEOUT_MS));
+        assertTrue(textView.wait(Until.textEquals("keycode dpad up pressed; "), TIMEOUT_MS));
     }
 
     @Test
@@ -194,7 +194,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressDPadLeft();
-        assertTrue(textView.wait(Until.textEquals("keycode dpad left pressed"), TIMEOUT_MS));
+        assertTrue(textView.wait(Until.textEquals("keycode dpad left pressed; "), TIMEOUT_MS));
     }
 
     @Test
@@ -203,7 +203,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressDPadRight();
-        assertTrue(textView.wait(Until.textEquals("keycode dpad right pressed"), TIMEOUT_MS));
+        assertTrue(textView.wait(Until.textEquals("keycode dpad right pressed; "), TIMEOUT_MS));
     }
 
     @Test
@@ -212,7 +212,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressDelete();
-        assertTrue(textView.wait(Until.textEquals("keycode delete pressed"), TIMEOUT_MS));
+        assertTrue(textView.wait(Until.textEquals("keycode delete pressed; "), TIMEOUT_MS));
     }
 
     @Test
@@ -221,7 +221,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressEnter();
-        assertTrue(textView.wait(Until.textEquals("keycode enter pressed"), TIMEOUT_MS));
+        assertTrue(textView.wait(Until.textEquals("keycode enter pressed; "), TIMEOUT_MS));
     }
 
     @Test
@@ -230,7 +230,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
-        assertTrue(textView.wait(Until.textEquals("keycode 0 pressed"), TIMEOUT_MS));
+        assertTrue(textView.wait(Until.textEquals("keycode 0 pressed; "), TIMEOUT_MS));
     }
 
     @Test
@@ -240,7 +240,31 @@
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressKeyCode(KeyEvent.KEYCODE_Z,
                 KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
-        assertTrue(textView.wait(Until.textEquals("keycode Z pressed with meta shift left on"),
+        assertTrue(textView.wait(Until.textEquals("keycode Z pressed; with meta shift left on; "),
+                TIMEOUT_MS));
+    }
+
+    @Test
+    public void testPressKeyCodes_withMetaKeyCodes() {
+        launchTestActivity(KeycodeTestActivity.class);
+
+        UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
+        mDevice.pressKeyCodes(new int[]{KeyEvent.KEYCODE_Z,
+                KeyEvent.KEYCODE_SHIFT_LEFT});
+        assertTrue(textView.wait(Until.textEquals(
+                "keycode Z pressed; keycode shift left pressed; with meta shift left on; "),
+                TIMEOUT_MS));
+    }
+
+    @Test
+    public void testPressKeyCodes_withMetaKeyCodesReverseOrder() {
+        launchTestActivity(KeycodeTestActivity.class);
+
+        UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
+        mDevice.pressKeyCodes(new int[]{KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_Z});
+        assertTrue(textView.wait(Until.textEquals(
+                "keycode shift left pressed; with meta shift left on; keycode Z pressed;"
+                        + " with meta shift left on; "),
                 TIMEOUT_MS));
     }
 
@@ -261,7 +285,7 @@
 
         UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
         mDevice.pressKeyCodes(new int[]{KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_B});
-        assertTrue(textView.wait(Until.textEquals("keycode A and keycode B are pressed"),
+        assertTrue(textView.wait(Until.textEquals("keycode A pressed; keycode B pressed; "),
                 TIMEOUT_MS));
     }
 
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/KeycodeTestActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/KeycodeTestActivity.java
index a4e91d8..d162bf2 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/KeycodeTestActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/KeycodeTestActivity.java
@@ -31,6 +31,8 @@
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.keycode_test_activity);
+        TextView textView = (TextView) findViewById(R.id.text_view);
+        textView.setText("");
     }
 
     @Override
@@ -38,55 +40,54 @@
         TextView textView = (TextView) findViewById(R.id.text_view);
         switch (keyCode) {
             case KeyEvent.KEYCODE_BACK:
-                textView.setText("keycode back pressed");
+                textView.append("keycode back pressed; ");
                 break;
             case KeyEvent.KEYCODE_DEL:
-                textView.setText("keycode delete pressed");
+                textView.append("keycode delete pressed; ");
                 break;
             case KeyEvent.KEYCODE_DPAD_CENTER:
-                textView.setText("keycode dpad center pressed");
+                textView.append("keycode dpad center pressed; ");
                 break;
             case KeyEvent.KEYCODE_DPAD_DOWN:
-                textView.setText("keycode dpad down pressed");
+                textView.append("keycode dpad down pressed; ");
                 break;
             case KeyEvent.KEYCODE_DPAD_LEFT:
-                textView.setText("keycode dpad left pressed");
+                textView.append("keycode dpad left pressed; ");
                 break;
             case KeyEvent.KEYCODE_DPAD_RIGHT:
-                textView.setText("keycode dpad right pressed");
+                textView.append("keycode dpad right pressed; ");
                 break;
             case KeyEvent.KEYCODE_DPAD_UP:
-                textView.setText("keycode dpad up pressed");
+                textView.append("keycode dpad up pressed; ");
                 break;
             case KeyEvent.KEYCODE_ENTER:
-                textView.setText("keycode enter pressed");
+                textView.append("keycode enter pressed; ");
                 break;
             case KeyEvent.KEYCODE_MENU:
-                textView.setText("keycode menu pressed");
+                textView.append("keycode menu pressed; ");
                 break;
             case KeyEvent.KEYCODE_SEARCH:
-                textView.setText("keycode search pressed");
-                break;
-            case KeyEvent.KEYCODE_Z:
-                textView.setText("keycode Z pressed");
-                break;
-            case KeyEvent.KEYCODE_0:
-                textView.setText("keycode 0 pressed");
+                textView.append("keycode search pressed; ");
                 break;
             case KeyEvent.KEYCODE_A:
-                if (mLastPressedKeyCode == KeyEvent.KEYCODE_B) {
-                    textView.setText("keycode A and keycode B are pressed");
-                }
+                textView.append("keycode A pressed; ");
                 break;
             case KeyEvent.KEYCODE_B:
-                if (mLastPressedKeyCode == KeyEvent.KEYCODE_A) {
-                    textView.setText("keycode A and keycode B are pressed");
-                }
+                textView.append("keycode B pressed; ");
+                break;
+            case KeyEvent.KEYCODE_Z:
+                textView.append("keycode Z pressed; ");
+                break;
+            case KeyEvent.KEYCODE_0:
+                textView.append("keycode 0 pressed; ");
+                break;
+            case KeyEvent.KEYCODE_SHIFT_LEFT:
+                textView.append("keycode shift left pressed; ");
                 break;
         }
 
         if ((event.getMetaState() & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON)) != 0) {
-            textView.append(" with meta shift left on");
+            textView.append("with meta shift left on; ");
         }
 
         mLastPressedKeyCode = keyCode;
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
index d2c5eb7..9f71e70 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
@@ -34,6 +34,8 @@
 import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -61,6 +63,30 @@
     // Inserted after each motion event injection.
     private static final int MOTION_EVENT_INJECTION_DELAY_MILLIS = 5;
 
+    private static final Map<Integer, Integer> KEY_MODIFIER = new HashMap<>();
+
+    static {
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_SHIFT_LEFT,
+                KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_SHIFT_RIGHT,
+                KeyEvent.META_SHIFT_RIGHT_ON | KeyEvent.META_SHIFT_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_ALT_LEFT,
+                KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_ALT_RIGHT,
+                KeyEvent.META_ALT_RIGHT_ON | KeyEvent.META_ALT_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_SYM, KeyEvent.META_SYM_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_FUNCTION, KeyEvent.META_FUNCTION_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_CTRL_LEFT,
+                KeyEvent.META_CTRL_LEFT_ON | KeyEvent.META_CTRL_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_CTRL_RIGHT,
+                KeyEvent.META_CTRL_RIGHT_ON | KeyEvent.META_CTRL_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_META_LEFT, KeyEvent.META_META_LEFT_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_META_RIGHT, KeyEvent.META_META_RIGHT_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_CAPS_LOCK, KeyEvent.META_CAPS_LOCK_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_NUM_LOCK, KeyEvent.META_NUM_LOCK_ON);
+        KEY_MODIFIER.put(KeyEvent.KEYCODE_SCROLL_LOCK, KeyEvent.META_SCROLL_LOCK_ON);
+    }
+
     InteractionController(UiDevice device) {
         mDevice = device;
     }
@@ -403,6 +429,9 @@
     public boolean sendKeys(int[] keyCodes, int metaState) {
         final long eventTime = SystemClock.uptimeMillis();
         for (int keyCode : keyCodes) {
+            if (KEY_MODIFIER.containsKey(keyCode)) {
+                metaState |= KEY_MODIFIER.get(keyCode);
+            }
             KeyEvent downEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN,
                     keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
                     InputDevice.SOURCE_KEYBOARD);
@@ -417,6 +446,9 @@
             if (!injectEventSync(upEvent)) {
                 return false;
             }
+            if (KEY_MODIFIER.containsKey(keyCode)) {
+                metaState &= ~KEY_MODIFIER.get(keyCode);
+            }
         }
         return true;
     }
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
index 1c189a3..747addd 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
@@ -481,7 +481,8 @@
     }
 
     /**
-     * Presses one or more keys.
+     * Presses one or more keys. Keys that change meta state are supported, and will apply their
+     * meta state to following keys.
      * <br/>
      * For example, you can simulate taking a screenshot on the device by pressing both the
      * power and volume down keys.
@@ -497,7 +498,8 @@
     }
 
     /**
-     * Presses one or more keys.
+     * Presses one or more keys. Keys that change meta state are supported, and will apply their
+     * meta state to following keys.
      * <br/>
      * For example, you can simulate taking a screenshot on the device by pressing both the
      * power and volume down keys.
diff --git a/wear/compose/compose-foundation/build.gradle b/wear/compose/compose-foundation/build.gradle
index aa8f032..4844a72 100644
--- a/wear/compose/compose-foundation/build.gradle
+++ b/wear/compose/compose-foundation/build.gradle
@@ -32,14 +32,14 @@
 }
 
 dependencies {
-    api("androidx.compose.foundation:foundation:1.7.0-rc01")
-    api("androidx.compose.ui:ui:1.7.0-rc01")
-    api("androidx.compose.ui:ui-text:1.7.0-rc01")
-    api("androidx.compose.runtime:runtime:1.7.0-rc01")
+    api("androidx.compose.foundation:foundation:1.7.0")
+    api("androidx.compose.ui:ui:1.7.0")
+    api("androidx.compose.ui:ui-text:1.7.0")
+    api("androidx.compose.runtime:runtime:1.7.0")
 
     implementation(libs.kotlinStdlib)
-    implementation("androidx.compose.foundation:foundation-layout:1.7.0-rc01")
-    implementation("androidx.compose.ui:ui-util:1.7.0-rc01")
+    implementation("androidx.compose.foundation:foundation-layout:1.7.0")
+    implementation("androidx.compose.ui:ui-util:1.7.0")
     implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
     implementation("androidx.core:core:1.12.0")
     implementation("androidx.profileinstaller:profileinstaller:1.3.1")
diff --git a/wear/compose/compose-material-core/build.gradle b/wear/compose/compose-material-core/build.gradle
index e380531..df1b6c6 100644
--- a/wear/compose/compose-material-core/build.gradle
+++ b/wear/compose/compose-material-core/build.gradle
@@ -33,16 +33,16 @@
 }
 
 dependencies {
-    api("androidx.compose.foundation:foundation:1.7.0-rc01")
-    api("androidx.compose.ui:ui:1.7.0-rc01")
-    api("androidx.compose.ui:ui-text:1.7.0-rc01")
-    api("androidx.compose.runtime:runtime:1.7.0-rc01")
+    api("androidx.compose.foundation:foundation:1.7.0")
+    api("androidx.compose.ui:ui:1.7.0")
+    api("androidx.compose.ui:ui-text:1.7.0")
+    api("androidx.compose.runtime:runtime:1.7.0")
 
     implementation(libs.kotlinStdlib)
-    implementation("androidx.compose.animation:animation:1.7.0-rc01")
-    implementation("androidx.compose.material:material-icons-core:1.7.0-rc01")
-    implementation("androidx.compose.material:material-ripple:1.7.0-rc01")
-    implementation("androidx.compose.ui:ui-util:1.7.0-rc01")
+    implementation("androidx.compose.animation:animation:1.7.0")
+    implementation("androidx.compose.material:material-icons-core:1.7.0")
+    implementation("androidx.compose.material:material-ripple:1.7.0")
+    implementation("androidx.compose.ui:ui-util:1.7.0")
     implementation(project(":wear:compose:compose-foundation"))
     implementation("androidx.profileinstaller:profileinstaller:1.3.1")
 
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Resources.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Resources.kt
index 1a967f1..7b9faf1 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Resources.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Resources.kt
@@ -62,11 +62,11 @@
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @Composable
-fun screenHeightDp() = LocalContext.current.resources.configuration.screenHeightDp
+fun screenHeightDp() = LocalConfiguration.current.screenHeightDp
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @Composable
-fun screenWidthDp() = LocalContext.current.resources.configuration.screenWidthDp
+fun screenWidthDp() = LocalConfiguration.current.screenWidthDp
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @Composable
diff --git a/wear/compose/compose-material/build.gradle b/wear/compose/compose-material/build.gradle
index 864af0d..1d2fc79 100644
--- a/wear/compose/compose-material/build.gradle
+++ b/wear/compose/compose-material/build.gradle
@@ -31,17 +31,17 @@
 }
 
 dependencies {
-    api("androidx.compose.foundation:foundation:1.7.0-rc01")
-    api("androidx.compose.ui:ui:1.7.0-rc01")
-    api("androidx.compose.ui:ui-text:1.7.0-rc01")
-    api("androidx.compose.runtime:runtime:1.7.0-rc01")
+    api("androidx.compose.foundation:foundation:1.7.0")
+    api("androidx.compose.ui:ui:1.7.0")
+    api("androidx.compose.ui:ui-text:1.7.0")
+    api("androidx.compose.runtime:runtime:1.7.0")
     api(project(":wear:compose:compose-foundation"))
 
     implementation(libs.kotlinStdlib)
-    implementation("androidx.compose.animation:animation:1.7.0-rc01")
-    implementation("androidx.compose.material:material-icons-core:1.7.0-rc01")
-    implementation("androidx.compose.material:material-ripple:1.7.0-rc01")
-    implementation("androidx.compose.ui:ui-util:1.7.0-rc01")
+    implementation("androidx.compose.animation:animation:1.7.0")
+    implementation("androidx.compose.material:material-icons-core:1.7.0")
+    implementation("androidx.compose.material:material-ripple:1.7.0")
+    implementation("androidx.compose.ui:ui-util:1.7.0")
     implementation(project(":wear:compose:compose-material-core"))
     implementation("androidx.profileinstaller:profileinstaller:1.3.1")
     implementation("androidx.lifecycle:lifecycle-common:2.7.0")
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index 3b402a4..b0b63b9 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -1,6 +1,28 @@
 // Signature format: 4.0
 package androidx.wear.compose.material3 {
 
+  public final class AlertDialogDefaults {
+    method @androidx.compose.runtime.Composable public void BottomButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public void ConfirmButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public void DismissButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public void GroupSeparator();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues contentPadding(boolean hasBottomButton);
+    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getConfirmIcon();
+    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getDismissIcon();
+    method public float getEdgeButtonExtraTopPadding();
+    method public androidx.compose.foundation.layout.Arrangement.Vertical getVerticalArrangement();
+    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> ConfirmIcon;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> DismissIcon;
+    property public final androidx.compose.foundation.layout.Arrangement.Vertical VerticalArrangement;
+    property public final float edgeButtonExtraTopPadding;
+    field public static final androidx.wear.compose.material3.AlertDialogDefaults INSTANCE;
+  }
+
+  public final class AlertDialogKt {
+    method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
+    method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> confirmButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
+  }
+
   @RequiresApi(31) public final class AnimatedTextDefaults {
     field public static final int CacheSize = 5; // 0x5
     field public static final androidx.wear.compose.material3.AnimatedTextDefaults INSTANCE;
@@ -298,6 +320,45 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
   }
 
+  public final class ConfirmationColors {
+    ctor public ConfirmationColors(long iconColor, long iconContainerColor, long textColor);
+    method public long getIconColor();
+    method public long getIconContainerColor();
+    method public long getTextColor();
+    property public final long iconColor;
+    property public final long iconContainerColor;
+    property public final long textColor;
+  }
+
+  public final class ConfirmationDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors confirmationColors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors confirmationColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> curvedText(String text, optional androidx.wear.compose.foundation.CurvedTextStyle style);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors failureColors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors failureColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> failureText();
+    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getFailureIcon();
+    method public float getIconSize();
+    method public float getSmallIconSize();
+    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getSuccessIcon();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors successColors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors successColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> successText();
+    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> FailureIcon;
+    property public final float IconSize;
+    property public final float SmallIconSize;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> SuccessIcon;
+    field public static final long ConfirmationDurationMillis = 4000L; // 0xfa0L
+    field public static final androidx.wear.compose.material3.ConfirmationDefaults INSTANCE;
+  }
+
+  public final class ConfirmationKt {
+    method @androidx.compose.runtime.Composable public static void Confirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? text, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Confirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FailureConfirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void SuccessConfirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+  }
+
   public final class ContentColorKt {
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
@@ -401,8 +462,8 @@
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
     method public float iconSizeFor(float size);
-    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors iconToggleButtonColors();
-    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors iconToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconToggleButtonColors iconToggleButtonColors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconToggleButtonColors iconToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors(optional long contentColor, optional long disabledContentColor);
     property public final float DefaultButtonSize;
@@ -422,7 +483,7 @@
     method @androidx.compose.runtime.Composable public static void FilledIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void FilledTonalIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void OutlinedIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
   }
 
@@ -432,6 +493,26 @@
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
+  @androidx.compose.runtime.Immutable public final class IconToggleButtonColors {
+    ctor public IconToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
+    method public long getCheckedContainerColor();
+    method public long getCheckedContentColor();
+    method public long getDisabledCheckedContainerColor();
+    method public long getDisabledCheckedContentColor();
+    method public long getDisabledUncheckedContainerColor();
+    method public long getDisabledUncheckedContentColor();
+    method public long getUncheckedContainerColor();
+    method public long getUncheckedContentColor();
+    property public final long checkedContainerColor;
+    property public final long checkedContentColor;
+    property public final long disabledCheckedContainerColor;
+    property public final long disabledCheckedContentColor;
+    property public final long disabledUncheckedContainerColor;
+    property public final long disabledUncheckedContentColor;
+    property public final long uncheckedContainerColor;
+    property public final long uncheckedContentColor;
+  }
+
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.wear.compose.material3.ExperimentalWearMaterial3Api public final class InlineSliderColors {
     ctor public InlineSliderColors(long containerColor, long buttonIconColor, long selectedBarColor, long unselectedBarColor, long barSeparatorColor, long disabledContainerColor, long disabledButtonIconColor, long disabledSelectedBarColor, long disabledUnselectedBarColor, long disabledBarSeparatorColor);
     method public long getBarSeparatorColor();
@@ -548,6 +629,34 @@
     method public static androidx.wear.compose.material3.MotionScheme standardMotionScheme();
   }
 
+  public final class OpenOnPhoneDialogColors {
+    ctor public OpenOnPhoneDialogColors(long iconColor, long iconContainerColor, long progressIndicatorColor, long progressTrackColor, long textColor);
+    method public long getIconColor();
+    method public long getIconContainerColor();
+    method public long getProgressIndicatorColor();
+    method public long getProgressTrackColor();
+    method public long getTextColor();
+    property public final long iconColor;
+    property public final long iconContainerColor;
+    property public final long progressIndicatorColor;
+    property public final long progressTrackColor;
+    property public final long textColor;
+  }
+
+  public final class OpenOnPhoneDialogDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.OpenOnPhoneDialogColors colors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.OpenOnPhoneDialogColors colors(optional long iconColor, optional long iconContainerColor, optional long progressIndicatorColor, optional long progressTrackColor, optional long textColor);
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> curvedText(optional String text, optional androidx.wear.compose.foundation.CurvedTextStyle style);
+    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getIcon();
+    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> Icon;
+    field public static final long DurationMillis = 4000L; // 0xfa0L
+    field public static final androidx.wear.compose.material3.OpenOnPhoneDialogDefaults INSTANCE;
+  }
+
+  public final class OpenOnPhoneDialogKt {
+    method @androidx.compose.runtime.Composable public static void OpenOnPhoneDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.OpenOnPhoneDialogColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+  }
+
   public final class PickerDefaults {
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior flingBehavior(androidx.wear.compose.material3.PickerState state, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decay);
     method public float getGradientRatio();
@@ -1119,8 +1228,8 @@
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors outlinedTextButtonColors(optional long contentColor, optional long disabledContentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors textButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors textButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
-    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors textToggleButtonColors();
-    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors textToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextToggleButtonColors textToggleButtonColors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextToggleButtonColors textToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
     property public final float DefaultButtonSize;
     property public final float LargeButtonSize;
     property public final float SmallButtonSize;
@@ -1134,7 +1243,7 @@
 
   public final class TextButtonKt {
     method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.TextButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void TextToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void TextToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.TextToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
   }
 
   public final class TextKt {
@@ -1151,6 +1260,26 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
   }
 
+  @androidx.compose.runtime.Immutable public final class TextToggleButtonColors {
+    ctor public TextToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
+    method public long getCheckedContainerColor();
+    method public long getCheckedContentColor();
+    method public long getDisabledCheckedContainerColor();
+    method public long getDisabledCheckedContentColor();
+    method public long getDisabledUncheckedContainerColor();
+    method public long getDisabledUncheckedContentColor();
+    method public long getUncheckedContainerColor();
+    method public long getUncheckedContentColor();
+    property public final long checkedContainerColor;
+    property public final long checkedContentColor;
+    property public final long disabledCheckedContainerColor;
+    property public final long disabledCheckedContentColor;
+    property public final long disabledUncheckedContainerColor;
+    property public final long disabledUncheckedContentColor;
+    property public final long uncheckedContainerColor;
+    property public final long uncheckedContentColor;
+  }
+
   @androidx.compose.runtime.Immutable public final class TimePickerColors {
     ctor public TimePickerColors(long selectedPickerContentColor, long unselectedPickerContentColor, long separatorColor, long pickerLabelColor, long confirmButtonContentColor, long confirmButtonContainerColor);
     method public long getConfirmButtonContainerColor();
@@ -1221,26 +1350,6 @@
     method public abstract void time();
   }
 
-  @androidx.compose.runtime.Immutable public final class ToggleButtonColors {
-    ctor public ToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
-    method public long getCheckedContainerColor();
-    method public long getCheckedContentColor();
-    method public long getDisabledCheckedContainerColor();
-    method public long getDisabledCheckedContentColor();
-    method public long getDisabledUncheckedContainerColor();
-    method public long getDisabledUncheckedContentColor();
-    method public long getUncheckedContainerColor();
-    method public long getUncheckedContentColor();
-    property public final long checkedContainerColor;
-    property public final long checkedContentColor;
-    property public final long disabledCheckedContainerColor;
-    property public final long disabledCheckedContentColor;
-    property public final long disabledUncheckedContainerColor;
-    property public final long disabledUncheckedContentColor;
-    property public final long uncheckedContainerColor;
-    property public final long uncheckedContentColor;
-  }
-
   public fun interface TouchExplorationStateProvider {
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<java.lang.Boolean> touchExplorationState();
   }
@@ -1296,32 +1405,6 @@
 
 }
 
-package androidx.wear.compose.material3.dialog {
-
-  public final class AlertDialogDefaults {
-    method @androidx.compose.runtime.Composable public void BottomButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public void ConfirmButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public void DismissButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public void GroupSeparator();
-    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues contentPadding(boolean hasBottomButton);
-    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getConfirmIcon();
-    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getDismissIcon();
-    method public float getEdgeButtonExtraTopPadding();
-    method public androidx.compose.foundation.layout.Arrangement.Vertical getVerticalArrangement();
-    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> ConfirmIcon;
-    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> DismissIcon;
-    property public final androidx.compose.foundation.layout.Arrangement.Vertical VerticalArrangement;
-    property public final float edgeButtonExtraTopPadding;
-    field public static final androidx.wear.compose.material3.dialog.AlertDialogDefaults INSTANCE;
-  }
-
-  public final class AlertDialogKt {
-    method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
-    method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> confirmButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
-  }
-
-}
-
 package androidx.wear.compose.material3.lazy {
 
   public final class LazyColumnScrollTransformModifiersKt {
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index 3b402a4..b0b63b9 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -1,6 +1,28 @@
 // Signature format: 4.0
 package androidx.wear.compose.material3 {
 
+  public final class AlertDialogDefaults {
+    method @androidx.compose.runtime.Composable public void BottomButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public void ConfirmButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public void DismissButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public void GroupSeparator();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues contentPadding(boolean hasBottomButton);
+    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getConfirmIcon();
+    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getDismissIcon();
+    method public float getEdgeButtonExtraTopPadding();
+    method public androidx.compose.foundation.layout.Arrangement.Vertical getVerticalArrangement();
+    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> ConfirmIcon;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> DismissIcon;
+    property public final androidx.compose.foundation.layout.Arrangement.Vertical VerticalArrangement;
+    property public final float edgeButtonExtraTopPadding;
+    field public static final androidx.wear.compose.material3.AlertDialogDefaults INSTANCE;
+  }
+
+  public final class AlertDialogKt {
+    method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
+    method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> confirmButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
+  }
+
   @RequiresApi(31) public final class AnimatedTextDefaults {
     field public static final int CacheSize = 5; // 0x5
     field public static final androidx.wear.compose.material3.AnimatedTextDefaults INSTANCE;
@@ -298,6 +320,45 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
   }
 
+  public final class ConfirmationColors {
+    ctor public ConfirmationColors(long iconColor, long iconContainerColor, long textColor);
+    method public long getIconColor();
+    method public long getIconContainerColor();
+    method public long getTextColor();
+    property public final long iconColor;
+    property public final long iconContainerColor;
+    property public final long textColor;
+  }
+
+  public final class ConfirmationDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors confirmationColors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors confirmationColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> curvedText(String text, optional androidx.wear.compose.foundation.CurvedTextStyle style);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors failureColors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors failureColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> failureText();
+    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getFailureIcon();
+    method public float getIconSize();
+    method public float getSmallIconSize();
+    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getSuccessIcon();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors successColors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors successColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> successText();
+    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> FailureIcon;
+    property public final float IconSize;
+    property public final float SmallIconSize;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> SuccessIcon;
+    field public static final long ConfirmationDurationMillis = 4000L; // 0xfa0L
+    field public static final androidx.wear.compose.material3.ConfirmationDefaults INSTANCE;
+  }
+
+  public final class ConfirmationKt {
+    method @androidx.compose.runtime.Composable public static void Confirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? text, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Confirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FailureConfirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void SuccessConfirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+  }
+
   public final class ContentColorKt {
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
@@ -401,8 +462,8 @@
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
     method public float iconSizeFor(float size);
-    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors iconToggleButtonColors();
-    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors iconToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconToggleButtonColors iconToggleButtonColors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconToggleButtonColors iconToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors(optional long contentColor, optional long disabledContentColor);
     property public final float DefaultButtonSize;
@@ -422,7 +483,7 @@
     method @androidx.compose.runtime.Composable public static void FilledIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void FilledTonalIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void OutlinedIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
   }
 
@@ -432,6 +493,26 @@
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
+  @androidx.compose.runtime.Immutable public final class IconToggleButtonColors {
+    ctor public IconToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
+    method public long getCheckedContainerColor();
+    method public long getCheckedContentColor();
+    method public long getDisabledCheckedContainerColor();
+    method public long getDisabledCheckedContentColor();
+    method public long getDisabledUncheckedContainerColor();
+    method public long getDisabledUncheckedContentColor();
+    method public long getUncheckedContainerColor();
+    method public long getUncheckedContentColor();
+    property public final long checkedContainerColor;
+    property public final long checkedContentColor;
+    property public final long disabledCheckedContainerColor;
+    property public final long disabledCheckedContentColor;
+    property public final long disabledUncheckedContainerColor;
+    property public final long disabledUncheckedContentColor;
+    property public final long uncheckedContainerColor;
+    property public final long uncheckedContentColor;
+  }
+
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.wear.compose.material3.ExperimentalWearMaterial3Api public final class InlineSliderColors {
     ctor public InlineSliderColors(long containerColor, long buttonIconColor, long selectedBarColor, long unselectedBarColor, long barSeparatorColor, long disabledContainerColor, long disabledButtonIconColor, long disabledSelectedBarColor, long disabledUnselectedBarColor, long disabledBarSeparatorColor);
     method public long getBarSeparatorColor();
@@ -548,6 +629,34 @@
     method public static androidx.wear.compose.material3.MotionScheme standardMotionScheme();
   }
 
+  public final class OpenOnPhoneDialogColors {
+    ctor public OpenOnPhoneDialogColors(long iconColor, long iconContainerColor, long progressIndicatorColor, long progressTrackColor, long textColor);
+    method public long getIconColor();
+    method public long getIconContainerColor();
+    method public long getProgressIndicatorColor();
+    method public long getProgressTrackColor();
+    method public long getTextColor();
+    property public final long iconColor;
+    property public final long iconContainerColor;
+    property public final long progressIndicatorColor;
+    property public final long progressTrackColor;
+    property public final long textColor;
+  }
+
+  public final class OpenOnPhoneDialogDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.OpenOnPhoneDialogColors colors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.OpenOnPhoneDialogColors colors(optional long iconColor, optional long iconContainerColor, optional long progressIndicatorColor, optional long progressTrackColor, optional long textColor);
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> curvedText(optional String text, optional androidx.wear.compose.foundation.CurvedTextStyle style);
+    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getIcon();
+    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> Icon;
+    field public static final long DurationMillis = 4000L; // 0xfa0L
+    field public static final androidx.wear.compose.material3.OpenOnPhoneDialogDefaults INSTANCE;
+  }
+
+  public final class OpenOnPhoneDialogKt {
+    method @androidx.compose.runtime.Composable public static void OpenOnPhoneDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.OpenOnPhoneDialogColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+  }
+
   public final class PickerDefaults {
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior flingBehavior(androidx.wear.compose.material3.PickerState state, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decay);
     method public float getGradientRatio();
@@ -1119,8 +1228,8 @@
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors outlinedTextButtonColors(optional long contentColor, optional long disabledContentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors textButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors textButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
-    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors textToggleButtonColors();
-    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors textToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextToggleButtonColors textToggleButtonColors();
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextToggleButtonColors textToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
     property public final float DefaultButtonSize;
     property public final float LargeButtonSize;
     property public final float SmallButtonSize;
@@ -1134,7 +1243,7 @@
 
   public final class TextButtonKt {
     method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.TextButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void TextToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void TextToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.TextToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
   }
 
   public final class TextKt {
@@ -1151,6 +1260,26 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
   }
 
+  @androidx.compose.runtime.Immutable public final class TextToggleButtonColors {
+    ctor public TextToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
+    method public long getCheckedContainerColor();
+    method public long getCheckedContentColor();
+    method public long getDisabledCheckedContainerColor();
+    method public long getDisabledCheckedContentColor();
+    method public long getDisabledUncheckedContainerColor();
+    method public long getDisabledUncheckedContentColor();
+    method public long getUncheckedContainerColor();
+    method public long getUncheckedContentColor();
+    property public final long checkedContainerColor;
+    property public final long checkedContentColor;
+    property public final long disabledCheckedContainerColor;
+    property public final long disabledCheckedContentColor;
+    property public final long disabledUncheckedContainerColor;
+    property public final long disabledUncheckedContentColor;
+    property public final long uncheckedContainerColor;
+    property public final long uncheckedContentColor;
+  }
+
   @androidx.compose.runtime.Immutable public final class TimePickerColors {
     ctor public TimePickerColors(long selectedPickerContentColor, long unselectedPickerContentColor, long separatorColor, long pickerLabelColor, long confirmButtonContentColor, long confirmButtonContainerColor);
     method public long getConfirmButtonContainerColor();
@@ -1221,26 +1350,6 @@
     method public abstract void time();
   }
 
-  @androidx.compose.runtime.Immutable public final class ToggleButtonColors {
-    ctor public ToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
-    method public long getCheckedContainerColor();
-    method public long getCheckedContentColor();
-    method public long getDisabledCheckedContainerColor();
-    method public long getDisabledCheckedContentColor();
-    method public long getDisabledUncheckedContainerColor();
-    method public long getDisabledUncheckedContentColor();
-    method public long getUncheckedContainerColor();
-    method public long getUncheckedContentColor();
-    property public final long checkedContainerColor;
-    property public final long checkedContentColor;
-    property public final long disabledCheckedContainerColor;
-    property public final long disabledCheckedContentColor;
-    property public final long disabledUncheckedContainerColor;
-    property public final long disabledUncheckedContentColor;
-    property public final long uncheckedContainerColor;
-    property public final long uncheckedContentColor;
-  }
-
   public fun interface TouchExplorationStateProvider {
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<java.lang.Boolean> touchExplorationState();
   }
@@ -1296,32 +1405,6 @@
 
 }
 
-package androidx.wear.compose.material3.dialog {
-
-  public final class AlertDialogDefaults {
-    method @androidx.compose.runtime.Composable public void BottomButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public void ConfirmButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public void DismissButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public void GroupSeparator();
-    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues contentPadding(boolean hasBottomButton);
-    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getConfirmIcon();
-    method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getDismissIcon();
-    method public float getEdgeButtonExtraTopPadding();
-    method public androidx.compose.foundation.layout.Arrangement.Vertical getVerticalArrangement();
-    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> ConfirmIcon;
-    property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> DismissIcon;
-    property public final androidx.compose.foundation.layout.Arrangement.Vertical VerticalArrangement;
-    property public final float edgeButtonExtraTopPadding;
-    field public static final androidx.wear.compose.material3.dialog.AlertDialogDefaults INSTANCE;
-  }
-
-  public final class AlertDialogKt {
-    method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
-    method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> confirmButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
-  }
-
-}
-
 package androidx.wear.compose.material3.lazy {
 
   public final class LazyColumnScrollTransformModifiersKt {
diff --git a/wear/compose/compose-material3/build.gradle b/wear/compose/compose-material3/build.gradle
index f103f85..43171ec 100644
--- a/wear/compose/compose-material3/build.gradle
+++ b/wear/compose/compose-material3/build.gradle
@@ -32,21 +32,22 @@
 }
 
 dependencies {
-    api("androidx.compose.foundation:foundation:1.7.0-rc01")
-    api("androidx.compose.ui:ui:1.7.0-rc01")
-    api("androidx.compose.ui:ui-text:1.7.0-rc01")
-    api("androidx.compose.runtime:runtime:1.7.0-rc01")
+    api("androidx.compose.foundation:foundation:1.7.0")
+    api("androidx.compose.ui:ui:1.7.0")
+    api("androidx.compose.ui:ui-text:1.7.0")
+    api("androidx.compose.runtime:runtime:1.7.0")
     api(project(":wear:compose:compose-foundation"))
 
     implementation(libs.kotlinStdlib)
     implementation(libs.kotlinCoroutinesCore)
-    implementation("androidx.compose.animation:animation:1.7.0-rc01")
-    implementation("androidx.compose.material:material-icons-core:1.7.0-rc01")
-    implementation("androidx.compose.material:material-ripple:1.7.0-rc01")
-    implementation("androidx.compose.ui:ui-util:1.7.0-rc01")
+    implementation("androidx.compose.animation:animation:1.7.0")
+    implementation("androidx.compose.material:material-icons-core:1.7.0")
+    implementation("androidx.compose.material:material-ripple:1.7.0")
+    implementation("androidx.compose.ui:ui-util:1.7.0")
     implementation(project(":wear:compose:compose-material-core"))
     implementation("androidx.profileinstaller:profileinstaller:1.3.1")
     implementation("androidx.graphics:graphics-shapes:1.0.0-beta01")
+    implementation project(':compose:animation:animation-graphics')
 
     androidTestImplementation(project(":compose:ui:ui-test"))
     androidTestImplementation(project(":compose:ui:ui-test-junit4"))
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/dialogs/AlertDialogs.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/AlertDialogs.kt
similarity index 95%
rename from wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/dialogs/AlertDialogs.kt
rename to wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/AlertDialogs.kt
index c71f190..4440425 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/dialogs/AlertDialogs.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/AlertDialogs.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.wear.compose.material3.demos.dialogs
+package androidx.wear.compose.material3.demos
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
@@ -38,6 +38,8 @@
 import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
 import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
 import androidx.wear.compose.integration.demos.common.ComposableDemo
+import androidx.wear.compose.material3.AlertDialog
+import androidx.wear.compose.material3.AlertDialogDefaults
 import androidx.wear.compose.material3.Button
 import androidx.wear.compose.material3.ListHeader
 import androidx.wear.compose.material3.MaterialTheme
@@ -45,11 +47,9 @@
 import androidx.wear.compose.material3.ScreenScaffold
 import androidx.wear.compose.material3.SwitchButton
 import androidx.wear.compose.material3.Text
-import androidx.wear.compose.material3.dialog.AlertDialog
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults
-import androidx.wear.compose.material3.samples.dialog.AlertDialogWithBottomButtonSample
-import androidx.wear.compose.material3.samples.dialog.AlertDialogWithConfirmAndDismissSample
-import androidx.wear.compose.material3.samples.dialog.AlertDialogWithContentGroupsSample
+import androidx.wear.compose.material3.samples.AlertDialogWithBottomButtonSample
+import androidx.wear.compose.material3.samples.AlertDialogWithConfirmAndDismissSample
+import androidx.wear.compose.material3.samples.AlertDialogWithContentGroupsSample
 
 val AlertDialogs =
     listOf(
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/Confirmations.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/Confirmations.kt
new file mode 100644
index 0000000..9233b25
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/Confirmations.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2024 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.wear.compose.material3.demos
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+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.wear.compose.integration.demos.common.ComposableDemo
+import androidx.wear.compose.material3.Confirmation
+import androidx.wear.compose.material3.ConfirmationDefaults
+import androidx.wear.compose.material3.FilledTonalButton
+import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.samples.ConfirmationSample
+import androidx.wear.compose.material3.samples.FailureConfirmationSample
+import androidx.wear.compose.material3.samples.LongTextConfirmationSample
+import androidx.wear.compose.material3.samples.SuccessConfirmationSample
+
+val Comfirmations =
+    listOf(
+        ComposableDemo("Generic confirmation") { ConfirmationSample() },
+        ComposableDemo("Long content confirmation") { LongTextConfirmationSample() },
+        ComposableDemo("Success confirmation") { SuccessConfirmationSample() },
+        ComposableDemo("Failure confirmation") { FailureConfirmationSample() },
+        ComposableDemo("Confirmation without text") { ConfirmationWithoutText() },
+        ComposableDemo("Confirmation with custom colors") { ConfirmationWithCustomColors() },
+    )
+
+@Composable
+fun ConfirmationWithoutText() {
+    var showConfirmation by remember { mutableStateOf(false) }
+
+    Box(Modifier.fillMaxSize()) {
+        FilledTonalButton(
+            modifier = Modifier.align(Alignment.Center),
+            onClick = { showConfirmation = true },
+            label = { Text("Show Confirmation") }
+        )
+    }
+
+    Confirmation(
+        show = showConfirmation,
+        onDismissRequest = { showConfirmation = false },
+        curvedText = null
+    ) {
+        Icon(
+            imageVector = Icons.Filled.Add,
+            contentDescription = null,
+            modifier = Modifier.size(ConfirmationDefaults.IconSize).align(Alignment.Center),
+            tint = MaterialTheme.colorScheme.primary
+        )
+    }
+}
+
+@Composable
+fun ConfirmationWithCustomColors() {
+    var showConfirmation by remember { mutableStateOf(false) }
+
+    Box(Modifier.fillMaxSize()) {
+        FilledTonalButton(
+            modifier = Modifier.align(Alignment.Center),
+            onClick = { showConfirmation = true },
+            label = { Text("Show Confirmation") }
+        )
+    }
+
+    Confirmation(
+        show = showConfirmation,
+        onDismissRequest = { showConfirmation = false },
+        colors =
+            ConfirmationDefaults.confirmationColors(
+                iconColor = MaterialTheme.colorScheme.tertiary,
+                iconContainerColor = MaterialTheme.colorScheme.onTertiary,
+                textColor = MaterialTheme.colorScheme.onSurfaceVariant
+            ),
+        curvedText = ConfirmationDefaults.curvedText("Custom confirmation")
+    ) {
+        Icon(
+            imageVector = Icons.Filled.Add,
+            contentDescription = null,
+            modifier = Modifier.size(ConfirmationDefaults.IconSize).align(Alignment.Center),
+        )
+    }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/OpenOnPhoneDialogDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/OpenOnPhoneDialogDemo.kt
new file mode 100644
index 0000000..ab628e5
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/OpenOnPhoneDialogDemo.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024 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.wear.compose.material3.demos
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+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.wear.compose.integration.demos.common.ComposableDemo
+import androidx.wear.compose.material3.FilledTonalButton
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.OpenOnPhoneDialog
+import androidx.wear.compose.material3.OpenOnPhoneDialogDefaults
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.samples.OpenOnPhoneDialogSample
+
+val OpenOnPhoneDialogDemos =
+    listOf(
+        ComposableDemo("Default OpenOnPhone Dialog") { OpenOnPhoneDialogSample() },
+        ComposableDemo("With custom text") { OpenOnPhoneDialogWithCustomText() },
+        ComposableDemo("With custom colors") { OpenOnPhoneDialogWithCustomColors() },
+    )
+
+@Composable
+fun OpenOnPhoneDialogWithCustomText() {
+    var showConfirmation by remember { mutableStateOf(false) }
+
+    Box(Modifier.fillMaxSize()) {
+        FilledTonalButton(
+            modifier = Modifier.align(Alignment.Center),
+            onClick = { showConfirmation = true },
+            label = { Text("Open on phone") }
+        )
+    }
+
+    OpenOnPhoneDialog(
+        show = showConfirmation,
+        onDismissRequest = { showConfirmation = false },
+        curvedText = OpenOnPhoneDialogDefaults.curvedText("Custom text")
+    )
+}
+
+@Composable
+fun OpenOnPhoneDialogWithCustomColors() {
+    var showConfirmation by remember { mutableStateOf(false) }
+
+    Box(Modifier.fillMaxSize()) {
+        FilledTonalButton(
+            modifier = Modifier.align(Alignment.Center),
+            onClick = { showConfirmation = true },
+            label = { Text("Open on phone") }
+        )
+    }
+
+    OpenOnPhoneDialog(
+        show = showConfirmation,
+        onDismissRequest = { showConfirmation = false },
+        colors =
+            OpenOnPhoneDialogDefaults.colors(
+                iconColor = MaterialTheme.colorScheme.tertiary,
+                iconContainerColor = MaterialTheme.colorScheme.tertiaryContainer,
+                progressIndicatorColor = MaterialTheme.colorScheme.tertiary,
+                progressTrackColor = MaterialTheme.colorScheme.onTertiary,
+                textColor = MaterialTheme.colorScheme.onSurfaceVariant
+            )
+    )
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
index 082ed2d..2f9ce14 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
@@ -22,7 +22,6 @@
 import androidx.wear.compose.integration.demos.common.Centralize
 import androidx.wear.compose.integration.demos.common.ComposableDemo
 import androidx.wear.compose.integration.demos.common.Material3DemoCategory
-import androidx.wear.compose.material3.demos.dialogs.AlertDialogs
 import androidx.wear.compose.material3.samples.AnimatedTextSample
 import androidx.wear.compose.material3.samples.AnimatedTextSampleButtonResponse
 import androidx.wear.compose.material3.samples.AnimatedTextSampleSharedFontRegistry
@@ -46,12 +45,9 @@
         listOf(
             ComposableDemo("Color Scheme") { ColorSchemeDemos() },
             Material3DemoCategory("Curved Text", CurvedTextDemos),
-            Material3DemoCategory(
-                "Dialogs",
-                listOf(
-                    Material3DemoCategory("AlertDialog", AlertDialogs),
-                )
-            ),
+            Material3DemoCategory("Alert Dialog", AlertDialogs),
+            Material3DemoCategory("Confirmation", Comfirmations),
+            Material3DemoCategory("Open on phone Dialog", OpenOnPhoneDialogDemos),
             ComposableDemo("Scaffold") { ScaffoldSample() },
             Material3DemoCategory("ScrollAway", ScrollAwayDemos),
             ComposableDemo("Haptics") { Centralize { HapticsDemos() } },
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/dialog/AlertDialogSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/AlertDialogSample.kt
similarity index 96%
rename from wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/dialog/AlertDialogSample.kt
rename to wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/AlertDialogSample.kt
index e20161b..dae4eef 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/dialog/AlertDialogSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/AlertDialogSample.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.wear.compose.material3.samples.dialog
+package androidx.wear.compose.material3.samples
 
 import androidx.annotation.Sampled
 import androidx.compose.foundation.layout.Box
@@ -31,12 +31,12 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material3.AlertDialog
+import androidx.wear.compose.material3.AlertDialogDefaults
 import androidx.wear.compose.material3.FilledTonalButton
 import androidx.wear.compose.material3.Icon
 import androidx.wear.compose.material3.MaterialTheme
 import androidx.wear.compose.material3.Text
-import androidx.wear.compose.material3.dialog.AlertDialog
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults
 
 @Sampled
 @Composable
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ConfirmationSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ConfirmationSample.kt
new file mode 100644
index 0000000..46bde32
--- /dev/null
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ConfirmationSample.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2024 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.wear.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+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.wear.compose.material3.Confirmation
+import androidx.wear.compose.material3.ConfirmationDefaults
+import androidx.wear.compose.material3.FailureConfirmation
+import androidx.wear.compose.material3.FilledTonalButton
+import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.SuccessConfirmation
+import androidx.wear.compose.material3.Text
+
+@Sampled
+@Composable
+fun ConfirmationSample() {
+    var showConfirmation by remember { mutableStateOf(false) }
+
+    Box(Modifier.fillMaxSize()) {
+        FilledTonalButton(
+            modifier = Modifier.align(Alignment.Center),
+            onClick = { showConfirmation = true },
+            label = { Text("Show Confirmation") }
+        )
+    }
+
+    // Has an icon and a short curved text content, which will be displayed along the bottom edge of
+    // the screen.
+    Confirmation(
+        show = showConfirmation,
+        onDismissRequest = { showConfirmation = false },
+        curvedText = ConfirmationDefaults.curvedText("Confirmed")
+    ) {
+        Icon(
+            imageVector = Icons.Filled.Add,
+            contentDescription = null,
+            modifier = Modifier.size(ConfirmationDefaults.IconSize),
+        )
+    }
+}
+
+@Sampled
+@Composable
+fun LongTextConfirmationSample() {
+    var showConfirmation by remember { mutableStateOf(false) }
+
+    Box(Modifier.fillMaxSize()) {
+        FilledTonalButton(
+            modifier = Modifier.align(Alignment.Center),
+            onClick = { showConfirmation = true },
+            label = { Text("Show Confirmation") }
+        )
+    }
+
+    // Has an icon and a text content. Text will be displayed in the center of the screen below the
+    // icon.
+    Confirmation(
+        show = showConfirmation,
+        onDismissRequest = { showConfirmation = false },
+        text = { Text(text = "Your message has been sent") },
+    ) {
+        Icon(
+            imageVector = Icons.Filled.Add,
+            contentDescription = null,
+            modifier = Modifier.size(ConfirmationDefaults.SmallIconSize),
+        )
+    }
+}
+
+@Sampled
+@Composable
+fun FailureConfirmationSample() {
+    var showConfirmation by remember { mutableStateOf(false) }
+
+    Box(Modifier.fillMaxSize()) {
+        FilledTonalButton(
+            modifier = Modifier.align(Alignment.Center),
+            onClick = { showConfirmation = true },
+            label = { Text("Show Confirmation") }
+        )
+    }
+
+    FailureConfirmation(show = showConfirmation, onDismissRequest = { showConfirmation = false })
+}
+
+@Sampled
+@Composable
+fun SuccessConfirmationSample() {
+    var showConfirmation by remember { mutableStateOf(false) }
+
+    Box(Modifier.fillMaxSize()) {
+        FilledTonalButton(
+            modifier = Modifier.align(Alignment.Center),
+            onClick = { showConfirmation = true },
+            label = { Text("Show Confirmation") }
+        )
+    }
+
+    SuccessConfirmation(show = showConfirmation, onDismissRequest = { showConfirmation = false })
+}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/OpenOnPhoneDialogSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/OpenOnPhoneDialogSample.kt
new file mode 100644
index 0000000..741132e
--- /dev/null
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/OpenOnPhoneDialogSample.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 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.wear.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+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.wear.compose.material3.FilledTonalButton
+import androidx.wear.compose.material3.OpenOnPhoneDialog
+import androidx.wear.compose.material3.Text
+
+@Sampled
+@Composable
+fun OpenOnPhoneDialogSample() {
+    var showConfirmation by remember { mutableStateOf(false) }
+
+    Box(Modifier.fillMaxSize()) {
+        FilledTonalButton(
+            modifier = Modifier.align(Alignment.Center),
+            onClick = { showConfirmation = true },
+            label = { Text("Open on phone") }
+        )
+    }
+
+    OpenOnPhoneDialog(
+        show = showConfirmation,
+        onDismissRequest = { showConfirmation = false },
+    )
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogScreenshotTest.kt
similarity index 95%
rename from wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogScreenshotTest.kt
rename to wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogScreenshotTest.kt
index 0b7d1f8..e049442 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogScreenshotTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.wear.compose.material3.dialog
+package androidx.wear.compose.material3
 
 import android.content.res.Configuration
 import android.os.Build
@@ -41,14 +41,6 @@
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
-import androidx.wear.compose.material3.FilledTonalButton
-import androidx.wear.compose.material3.Icon
-import androidx.wear.compose.material3.SCREENSHOT_GOLDEN_PATH
-import androidx.wear.compose.material3.ScreenSize
-import androidx.wear.compose.material3.TEST_TAG
-import androidx.wear.compose.material3.Text
-import androidx.wear.compose.material3.goldenIdentifier
-import androidx.wear.compose.material3.setContentWithTheme
 import com.google.testing.junit.testparameterinjector.TestParameter
 import com.google.testing.junit.testparameterinjector.TestParameterInjector
 import org.junit.Rule
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogTest.kt
similarity index 96%
rename from wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogTest.kt
rename to wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogTest.kt
index 14069c6..6e59369 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.wear.compose.material3.dialog
+package androidx.wear.compose.material3
 
 import android.os.Build
 import androidx.compose.foundation.background
@@ -39,17 +39,6 @@
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.test.filters.SdkSuppress
-import androidx.wear.compose.material3.Button
-import androidx.wear.compose.material3.LocalContentColor
-import androidx.wear.compose.material3.LocalTextAlign
-import androidx.wear.compose.material3.LocalTextMaxLines
-import androidx.wear.compose.material3.LocalTextStyle
-import androidx.wear.compose.material3.MaterialTheme
-import androidx.wear.compose.material3.TEST_TAG
-import androidx.wear.compose.material3.TestImage
-import androidx.wear.compose.material3.Text
-import androidx.wear.compose.material3.setContentWithTheme
-import androidx.wear.compose.material3.setContentWithThemeForSizeAssertions
 import junit.framework.TestCase.assertEquals
 import org.junit.Rule
 import org.junit.Test
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationScreenshotTest.kt
new file mode 100644
index 0000000..73e047e
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationScreenshotTest.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2024 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.wear.compose.material3
+
+import android.content.res.Configuration
+import android.os.Build
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import androidx.wear.compose.material3.ConfirmationDefaults.curvedText
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(TestParameterInjector::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+class ConfirmationScreenshotTest {
+    @get:Rule val rule = createComposeRule()
+
+    @get:Rule val screenshotRule = AndroidXScreenshotTestRule(SCREENSHOT_GOLDEN_PATH)
+
+    @get:Rule val testName = TestName()
+
+    @Test
+    fun confirmation_icon_linearText(@TestParameter screenSize: ScreenSize) {
+
+        rule.verifyConfirmationScreenshot(
+            testName = testName,
+            screenshotRule = screenshotRule,
+            screenSize = screenSize
+        ) { modifier ->
+            Confirmation(
+                show = true,
+                modifier = modifier,
+                onDismissRequest = {},
+                text = { Text("Your message has been sent") }
+            ) {
+                DefaultSmallIcon()
+            }
+        }
+    }
+
+    @Test
+    fun confirmation_icon_curvedText(@TestParameter screenSize: ScreenSize) {
+        rule.verifyConfirmationScreenshot(
+            testName = testName,
+            screenshotRule = screenshotRule,
+            screenSize = screenSize
+        ) { modifier ->
+            Confirmation(
+                show = true,
+                modifier = modifier,
+                onDismissRequest = {},
+                curvedText = curvedText("Confirmed")
+            ) {
+                DefaultIcon()
+            }
+        }
+    }
+
+    @Test
+    fun confirmation_icon_noText(@TestParameter screenSize: ScreenSize) {
+        rule.verifyConfirmationScreenshot(
+            testName = testName,
+            screenshotRule = screenshotRule,
+            screenSize = screenSize
+        ) { modifier ->
+            Confirmation(
+                show = true,
+                modifier = modifier,
+                onDismissRequest = {},
+                curvedText = null
+            ) {
+                DefaultIcon()
+            }
+        }
+    }
+
+    @Test
+    fun successConfirmation_icon_text(@TestParameter screenSize: ScreenSize) {
+        rule.verifyConfirmationScreenshot(
+            testName = testName,
+            screenshotRule = screenshotRule,
+            screenSize = screenSize
+        ) { modifier ->
+            SuccessConfirmation(
+                show = true,
+                modifier = modifier,
+                onDismissRequest = {},
+                curvedText = curvedText("Success")
+            )
+        }
+    }
+
+    @Test
+    fun successConfirmation_icon_noText(@TestParameter screenSize: ScreenSize) {
+        rule.verifyConfirmationScreenshot(
+            testName = testName,
+            screenshotRule = screenshotRule,
+            screenSize = screenSize
+        ) { modifier ->
+            SuccessConfirmation(
+                show = true,
+                modifier = modifier,
+                onDismissRequest = {},
+                curvedText = null
+            )
+        }
+    }
+
+    @Test
+    fun failureConfirmation_icon_text(@TestParameter screenSize: ScreenSize) {
+        rule.verifyConfirmationScreenshot(
+            testName = testName,
+            screenshotRule = screenshotRule,
+            screenSize = screenSize
+        ) { modifier ->
+            FailureConfirmation(
+                show = true,
+                modifier = modifier,
+                onDismissRequest = {},
+                curvedText = curvedText("Failure")
+            )
+        }
+    }
+
+    @Test
+    fun failureConfirmation_icon_noText(@TestParameter screenSize: ScreenSize) {
+        rule.verifyConfirmationScreenshot(
+            testName = testName,
+            screenshotRule = screenshotRule,
+            screenSize = screenSize
+        ) { modifier ->
+            FailureConfirmation(
+                show = true,
+                modifier = modifier,
+                onDismissRequest = {},
+                curvedText = null,
+            )
+        }
+    }
+
+    private fun ComposeContentTestRule.verifyConfirmationScreenshot(
+        testName: TestName,
+        screenshotRule: AndroidXScreenshotTestRule,
+        screenSize: ScreenSize,
+        content: @Composable (modifier: Modifier) -> Unit
+    ) {
+        setContentWithTheme {
+            val originalConfiguration = LocalConfiguration.current
+            val originalContext = LocalContext.current
+            val fixedScreenSizeConfiguration =
+                remember(originalConfiguration) {
+                    Configuration(originalConfiguration).apply {
+                        screenWidthDp = screenSize.size
+                        screenHeightDp = screenSize.size
+                    }
+                }
+            originalContext.resources.configuration.updateFrom(fixedScreenSizeConfiguration)
+
+            CompositionLocalProvider(
+                LocalContext provides originalContext,
+                LocalConfiguration provides fixedScreenSizeConfiguration,
+            ) {
+                content(Modifier.size(screenSize.size.dp).testTag(TEST_TAG))
+            }
+        }
+
+        onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, testName.goldenIdentifier())
+    }
+
+    @Composable
+    private fun DefaultIcon() {
+        Icon(
+            Icons.Filled.Add,
+            modifier = Modifier.size(ConfirmationDefaults.IconSize),
+            tint = MaterialTheme.colorScheme.primary,
+            contentDescription = null
+        )
+    }
+
+    @Composable
+    private fun DefaultSmallIcon() {
+        Icon(
+            Icons.Filled.Add,
+            modifier = Modifier.size(ConfirmationDefaults.SmallIconSize),
+            tint = MaterialTheme.colorScheme.primary,
+            contentDescription = null
+        )
+    }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationTest.kt
new file mode 100644
index 0000000..31206a1
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationTest.kt
@@ -0,0 +1,605 @@
+/*
+ * Copyright 2024 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.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.background
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertContainsColor
+import androidx.compose.testutils.assertIsEqualTo
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeRight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.test.filters.SdkSuppress
+import org.junit.Rule
+import org.junit.Test
+
+class ConfirmationTest {
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun confirmation_linearText_supports_testtag() {
+        rule.setContentWithTheme {
+            Confirmation(
+                show = true,
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = {},
+                text = {},
+            ) {}
+        }
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun confirmation_curvedText_supports_testtag() {
+        rule.setContentWithTheme {
+            Confirmation(
+                show = true,
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = {},
+                curvedText = {}
+            ) {}
+        }
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun successConfirmation_supports_testtag() {
+        rule.setContentWithTheme {
+            SuccessConfirmation(
+                show = true,
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = {},
+            )
+        }
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun failureConfirmation_supports_testtag() {
+        rule.setContentWithTheme {
+            FailureConfirmation(
+                show = true,
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = {},
+            )
+        }
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun confirmation_linearText_supports_swipeToDismiss() {
+        rule.setContentWithTheme {
+            var showDialog by remember { mutableStateOf(true) }
+            Confirmation(
+                modifier = Modifier.testTag(TEST_TAG),
+                text = {},
+                onDismissRequest = { showDialog = false },
+                show = showDialog
+            ) {}
+        }
+
+        rule.onNodeWithTag(TEST_TAG).performTouchInput { swipeRight() }
+        rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+    }
+
+    @Test
+    fun confirmation_curvedText_supports_swipeToDismiss() {
+        rule.setContentWithTheme {
+            var showDialog by remember { mutableStateOf(true) }
+            Confirmation(
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = { showDialog = false },
+                show = showDialog,
+                curvedText = {}
+            ) {}
+        }
+
+        rule.onNodeWithTag(TEST_TAG).performTouchInput { swipeRight() }
+        rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+    }
+
+    @Test
+    fun successConfirmation_supports_swipeToDismiss() {
+        rule.mainClock.autoAdvance = false
+        rule.setContentWithTheme {
+            var showDialog by remember { mutableStateOf(true) }
+            SuccessConfirmation(
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = { showDialog = false },
+                show = showDialog,
+            )
+        }
+        // Advancing time so that animation will finish its motion.
+        rule.mainClock.advanceTimeBy(1000)
+        rule.onNodeWithTag(TEST_TAG).performTouchInput { swipeRight() }
+        rule.mainClock.advanceTimeBy(1000)
+        rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+    }
+
+    @Test
+    fun failureConfirmation_supports_swipeToDismiss() {
+        rule.mainClock.autoAdvance = false
+        rule.setContentWithTheme {
+            var showDialog by remember { mutableStateOf(true) }
+            FailureConfirmation(
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = { showDialog = false },
+                show = showDialog,
+            )
+        }
+        // Advancing time so that animation will finish its motion.
+        rule.mainClock.advanceTimeBy(1000)
+        rule.onNodeWithTag(TEST_TAG).performTouchInput { swipeRight() }
+        rule.mainClock.advanceTimeBy(1000)
+        rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+    }
+
+    @Test
+    fun hides_confirmation_linearText_when_show_false() {
+        rule.setContentWithTheme {
+            Confirmation(
+                show = false,
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = {},
+                text = {},
+            ) {}
+        }
+        rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+    }
+
+    @Test
+    fun hides_confirmation_curvedText_when_show_false() {
+        rule.setContentWithTheme {
+            Confirmation(
+                show = false,
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = {},
+                curvedText = {}
+            ) {}
+        }
+        rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+    }
+
+    @Test
+    fun hides_successConfirmation_when_show_false() {
+        rule.setContentWithTheme {
+            SuccessConfirmation(
+                show = false,
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = {},
+            )
+        }
+        rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+    }
+
+    @Test
+    fun hides_failureConfirmation_when_show_false() {
+        rule.setContentWithTheme {
+            FailureConfirmation(
+                show = false,
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = {},
+            )
+        }
+        rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+    }
+
+    @Test
+    fun confirmation_displays_icon_with_linearText() {
+        rule.setContentWithTheme {
+            Confirmation(
+                text = { Text("Text", modifier = Modifier.testTag(TextTestTag)) },
+                onDismissRequest = {},
+                show = true
+            ) {
+                TestImage(IconTestTag)
+            }
+        }
+        rule.onNodeWithTag(IconTestTag).assertExists()
+        rule.onNodeWithTag(TextTestTag).assertExists()
+    }
+
+    @Test
+    fun confirmation_displays_icon_with_curvedText() {
+        rule.setContentWithTheme {
+            Confirmation(
+                onDismissRequest = {},
+                show = true,
+                curvedText = { curvedText(CurvedText) }
+            ) {
+                TestImage(IconTestTag)
+            }
+        }
+        rule.onNodeWithTag(IconTestTag).assertExists()
+        rule.onNodeWithContentDescription(CurvedText).assertExists()
+    }
+
+    @Test
+    fun successConfirmation_displays_icon_with_text() {
+        rule.setContentWithTheme {
+            SuccessConfirmation(
+                onDismissRequest = {},
+                show = true,
+                curvedText = ConfirmationDefaults.curvedText(CurvedText)
+            ) {
+                TestImage(IconTestTag)
+            }
+        }
+        rule.onNodeWithTag(IconTestTag).assertExists()
+        rule.onNodeWithContentDescription(CurvedText).assertExists()
+    }
+
+    @Test
+    fun failureConfirmation_displays_icon_with_text() {
+        rule.setContentWithTheme {
+            FailureConfirmation(
+                onDismissRequest = {},
+                show = true,
+                curvedText = ConfirmationDefaults.curvedText(CurvedText)
+            ) {
+                TestImage(IconTestTag)
+            }
+        }
+        rule.onNodeWithTag(IconTestTag).assertExists()
+        rule.onNodeWithContentDescription(CurvedText).assertExists()
+    }
+
+    @Test
+    fun confirmation_linearText_dismissed_after_timeout() {
+        var dismissed = false
+        rule.mainClock.autoAdvance = false
+        rule.setContentWithTheme {
+            Confirmation(text = {}, onDismissRequest = { dismissed = true }, show = true) {}
+        }
+        // Timeout longer than default confirmation duration
+        rule.mainClock.advanceTimeBy(ConfirmationDefaults.ConfirmationDurationMillis + 1000)
+        assert(dismissed)
+    }
+
+    @Test
+    fun confirmation_curvedText_dismissed_after_timeout() {
+        var dismissed = false
+        rule.mainClock.autoAdvance = false
+        rule.setContentWithTheme {
+            Confirmation(onDismissRequest = { dismissed = true }, show = true, curvedText = {}) {}
+        }
+        // Timeout longer than default confirmation duration
+        rule.mainClock.advanceTimeBy(ConfirmationDefaults.ConfirmationDurationMillis + 1000)
+        assert(dismissed)
+    }
+
+    @Test
+    fun successConfirmation_dismissed_after_timeout() {
+        var dismissed = false
+        rule.mainClock.autoAdvance = false
+        rule.setContentWithTheme {
+            SuccessConfirmation(
+                onDismissRequest = { dismissed = true },
+                show = true,
+            )
+        }
+        // Timeout longer than default confirmation duration
+        rule.mainClock.advanceTimeBy(ConfirmationDefaults.ConfirmationDurationMillis + 1000)
+        assert(dismissed)
+    }
+
+    @Test
+    fun failureConfirmation_dismissed_after_timeout() {
+        var dismissed = false
+        rule.mainClock.autoAdvance = false
+        rule.setContentWithTheme {
+            FailureConfirmation(
+                onDismissRequest = { dismissed = true },
+                show = true,
+            )
+        }
+        // Timeout longer than default confirmation duration
+        rule.mainClock.advanceTimeBy(ConfirmationDefaults.ConfirmationDurationMillis + 1000)
+        assert(dismissed)
+    }
+
+    @Test
+    fun confirmation_linearText_positioning() {
+        rule.setContentWithThemeForSizeAssertions(useUnmergedTree = true) {
+            Confirmation(
+                show = true,
+                text = {
+                    Text(
+                        "Title",
+                        modifier = Modifier.testTag(TextTestTag),
+                        textAlign = TextAlign.Center
+                    )
+                },
+                onDismissRequest = {},
+                modifier = Modifier.testTag(TEST_TAG),
+            ) {
+                TestIcon(Modifier.testTag(IconTestTag))
+            }
+        }
+
+        // Calculating the center of the icon
+        val iconCenter =
+            rule.onNodeWithTag(IconTestTag).getUnclippedBoundsInRoot().run { (top + bottom) / 2 }
+        val textTop = rule.onNodeWithTag(TextTestTag).getUnclippedBoundsInRoot().top
+
+        // Stepping down half of the container height with vertical content padding
+        textTop.assertIsEqualTo(
+            iconCenter +
+                ConfirmationDefaults.ConfirmationIconContainerSmallSize / 2 +
+                ConfirmationDefaults.LinearContentSpacing
+        )
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    @Test
+    fun confirmation_linearText_correct_colors() {
+        var expectedIconColor: Color = Color.Unspecified
+        var expectedIconContainerColor: Color = Color.Unspecified
+        var expectedTextColor: Color = Color.Unspecified
+
+        rule.setContentWithTheme {
+            Confirmation(
+                onDismissRequest = {},
+                modifier = Modifier.testTag(TEST_TAG),
+                show = true,
+                text = { Text("Text") },
+            ) {
+                TestIcon(Modifier.testTag(IconTestTag))
+            }
+            expectedIconColor = MaterialTheme.colorScheme.primary
+            expectedIconContainerColor = MaterialTheme.colorScheme.onPrimary
+            expectedTextColor = MaterialTheme.colorScheme.onBackground
+        }
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedIconColor)
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertContainsColor(expectedIconContainerColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedTextColor)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    @Test
+    fun confirmation_curvedText_correct_colors() {
+        var expectedIconColor: Color = Color.Unspecified
+        var expectedIconContainerColor: Color = Color.Unspecified
+        var expectedTextColor: Color = Color.Unspecified
+        rule.setContentWithTheme {
+            Confirmation(
+                onDismissRequest = {},
+                modifier = Modifier.testTag(TEST_TAG),
+                show = true,
+                curvedText = ConfirmationDefaults.curvedText(CurvedText)
+            ) {
+                TestIcon(Modifier.testTag(IconTestTag))
+            }
+            expectedIconColor = MaterialTheme.colorScheme.primary
+            expectedIconContainerColor = MaterialTheme.colorScheme.onPrimary
+            expectedTextColor = MaterialTheme.colorScheme.onBackground
+        }
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedIconColor)
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertContainsColor(expectedIconContainerColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedTextColor)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    @Test
+    fun successConfirmation_correct_colors() {
+        var expectedIconColor: Color = Color.Unspecified
+        var expectedIconContainerColor: Color = Color.Unspecified
+        var expectedTextColor: Color = Color.Unspecified
+
+        rule.setContentWithTheme {
+            SuccessConfirmation(
+                onDismissRequest = {},
+                modifier = Modifier.testTag(TEST_TAG),
+                show = true,
+            )
+            expectedIconColor = MaterialTheme.colorScheme.primary
+            expectedIconContainerColor = MaterialTheme.colorScheme.onPrimary
+            expectedTextColor = MaterialTheme.colorScheme.onBackground
+        }
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedIconColor)
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertContainsColor(expectedIconContainerColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedTextColor)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    @Test
+    fun failureConfirmation_correct_colors() {
+        var expectedIconColor: Color = Color.Unspecified
+        var expectedIconContainerColor: Color = Color.Unspecified
+        var expectedTextColor: Color = Color.Unspecified
+        val backgroundColor = Color.Black
+        rule.setContentWithTheme {
+            FailureConfirmation(
+                onDismissRequest = {},
+                modifier = Modifier.testTag(TEST_TAG).background(backgroundColor),
+                show = true,
+            ) {
+                TestIcon(Modifier.testTag(IconTestTag))
+            }
+            expectedIconColor = MaterialTheme.colorScheme.errorContainer
+            // As we have .8 alpha, we have to merge this color with background
+            expectedIconContainerColor =
+                MaterialTheme.colorScheme.onErrorContainer.copy(.8f).compositeOver(backgroundColor)
+            expectedTextColor = MaterialTheme.colorScheme.onBackground
+        }
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedIconColor)
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertContainsColor(expectedIconContainerColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedTextColor)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    @Test
+    fun confirmation_linearText_custom_colors() {
+        val customIconColor: Color = Color.Red
+        val customIconContainerColor: Color = Color.Green
+        val customTextColor: Color = Color.Blue
+
+        rule.setContentWithTheme {
+            Confirmation(
+                onDismissRequest = {},
+                modifier = Modifier.testTag(TEST_TAG),
+                show = true,
+                text = { Text("Text") },
+                colors =
+                    ConfirmationDefaults.confirmationColors(
+                        iconColor = customIconColor,
+                        iconContainerColor = customIconContainerColor,
+                        textColor = customTextColor
+                    )
+            ) {
+                TestIcon(Modifier.testTag(IconTestTag))
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconContainerColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customTextColor)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    @Test
+    fun confirmation_curvedText_custom_colors() {
+        val customIconColor: Color = Color.Red
+        val customIconContainerColor: Color = Color.Green
+        val customTextColor: Color = Color.Blue
+
+        rule.setContentWithTheme {
+            Confirmation(
+                onDismissRequest = {},
+                modifier = Modifier.testTag(TEST_TAG),
+                show = true,
+                colors =
+                    ConfirmationDefaults.confirmationColors(
+                        iconColor = customIconColor,
+                        iconContainerColor = customIconContainerColor,
+                        textColor = customTextColor
+                    ),
+                curvedText = ConfirmationDefaults.curvedText(CurvedText)
+            ) {
+                TestIcon(Modifier.testTag(IconTestTag))
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconContainerColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customTextColor)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    @Test
+    fun successConfirmation_curvedText_custom_colors() {
+        val customIconColor: Color = Color.Red
+        val customIconContainerColor: Color = Color.Green
+        val customTextColor: Color = Color.Blue
+
+        rule.setContentWithTheme {
+            SuccessConfirmation(
+                onDismissRequest = {},
+                modifier = Modifier.testTag(TEST_TAG),
+                show = true,
+                colors =
+                    ConfirmationDefaults.successColors(
+                        iconColor = customIconColor,
+                        iconContainerColor = customIconContainerColor,
+                        textColor = customTextColor
+                    ),
+            ) {
+                TestIcon(Modifier.testTag(IconTestTag))
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconContainerColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customTextColor)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    @Test
+    fun failureConfirmation_curvedText_custom_colors() {
+        val customIconColor: Color = Color.Red
+        val customIconContainerColor: Color = Color.Green
+        val customTextColor: Color = Color.Blue
+
+        rule.setContentWithTheme {
+            FailureConfirmation(
+                onDismissRequest = {},
+                modifier = Modifier.testTag(TEST_TAG),
+                show = true,
+                colors =
+                    ConfirmationDefaults.failureColors(
+                        iconColor = customIconColor,
+                        iconContainerColor = customIconContainerColor,
+                        textColor = customTextColor
+                    ),
+            ) {
+                TestIcon(Modifier.testTag(IconTestTag))
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconContainerColor)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customTextColor)
+    }
+}
+
+private const val IconTestTag = "icon"
+private const val TextTestTag = "text"
+private const val CurvedText = "CurvedText"
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
index 188a8f8..9c0a3f0 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
@@ -618,7 +618,7 @@
     private fun ComposeContentTestRule.verifyIconToggleButtonColors(
         status: Status,
         checked: Boolean,
-        colors: @Composable () -> ToggleButtonColors,
+        colors: @Composable () -> IconToggleButtonColors,
         containerColor: @Composable () -> Color,
         contentColor: @Composable () -> Color,
     ) {
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogScreenshotTest.kt
new file mode 100644
index 0000000..74568a6
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogScreenshotTest.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2024 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.wear.compose.material3
+
+import android.content.res.Configuration
+import android.os.Build
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(TestParameterInjector::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+class OpenOnPhoneDialogScreenshotTest {
+    @get:Rule val rule = createComposeRule()
+
+    @get:Rule val screenshotRule = AndroidXScreenshotTestRule(SCREENSHOT_GOLDEN_PATH)
+
+    @get:Rule val testName = TestName()
+
+    @Test
+    fun openOnPhone_50_percent_progress(@TestParameter screenSize: ScreenSize) {
+        rule.verifyOpenOnPhoneScreenshot(
+            testName = testName,
+            screenshotRule = screenshotRule,
+            advanceTimeBy = OpenOnPhoneDialogDefaults.DurationMillis / 2,
+            screenSize = screenSize
+        )
+    }
+
+    @Test
+    fun openOnPhone_100_percent_progress(@TestParameter screenSize: ScreenSize) {
+        rule.verifyOpenOnPhoneScreenshot(
+            testName = testName,
+            screenshotRule = screenshotRule,
+            advanceTimeBy = OpenOnPhoneDialogDefaults.DurationMillis,
+            screenSize = screenSize
+        )
+    }
+
+    private fun ComposeContentTestRule.verifyOpenOnPhoneScreenshot(
+        testName: TestName,
+        screenshotRule: AndroidXScreenshotTestRule,
+        screenSize: ScreenSize,
+        advanceTimeBy: Long,
+    ) {
+        rule.mainClock.autoAdvance = false
+        setContentWithTheme {
+            val originalConfiguration = LocalConfiguration.current
+            val originalContext = LocalContext.current
+            val fixedScreenSizeConfiguration =
+                remember(originalConfiguration) {
+                    Configuration(originalConfiguration).apply {
+                        screenWidthDp = screenSize.size
+                        screenHeightDp = screenSize.size
+                    }
+                }
+            originalContext.resources.configuration.updateFrom(fixedScreenSizeConfiguration)
+
+            CompositionLocalProvider(
+                LocalContext provides originalContext,
+                LocalConfiguration provides fixedScreenSizeConfiguration,
+            ) {
+                OpenOnPhoneDialog(
+                    show = true,
+                    modifier = Modifier.size(screenSize.size.dp).testTag(TEST_TAG),
+                    onDismissRequest = {},
+                )
+            }
+        }
+
+        rule.mainClock.advanceTimeBy(advanceTimeBy)
+        onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, testName.goldenIdentifier())
+    }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogTest.kt
new file mode 100644
index 0000000..c5de9c1
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2024 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.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertContainsColor
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+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.test.performTouchInput
+import androidx.compose.ui.test.swipeRight
+import androidx.test.filters.SdkSuppress
+import org.junit.Rule
+import org.junit.Test
+
+class OpenOnPhoneDialogTest {
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun openOnPhone_supports_testtag() {
+        rule.setContentWithTheme {
+            OpenOnPhoneDialog(
+                show = true,
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = {},
+            )
+        }
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun openOnPhone_supports_swipeToDismiss() {
+        rule.mainClock.autoAdvance = false
+        rule.setContentWithTheme {
+            var showDialog by remember { mutableStateOf(true) }
+            OpenOnPhoneDialog(
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = { showDialog = false },
+                show = showDialog
+            )
+        }
+        rule.mainClock.advanceTimeBy(OpenOnPhoneDialogDefaults.DurationMillis / 2)
+        rule.onNodeWithTag(TEST_TAG).performTouchInput({ swipeRight() })
+        // Advancing time so that the dialog is dismissed
+        rule.mainClock.advanceTimeBy(300)
+        rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+    }
+
+    @Test
+    fun hides_openOnPhone_when_show_false() {
+        rule.setContentWithTheme {
+            OpenOnPhoneDialog(
+                show = false,
+                modifier = Modifier.testTag(TEST_TAG),
+                onDismissRequest = {},
+            )
+        }
+        rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+    }
+
+    @Test
+    fun openOnPhone_displays_icon() {
+        rule.setContentWithTheme {
+            OpenOnPhoneDialog(onDismissRequest = {}, show = true) { TestImage(IconTestTag) }
+        }
+        rule.onNodeWithTag(IconTestTag).assertExists()
+    }
+
+    @Test
+    fun openOnPhone_dismissed_after_timeout() {
+        var dismissed = false
+        rule.mainClock.autoAdvance = false
+        rule.setContentWithTheme {
+            OpenOnPhoneDialog(onDismissRequest = { dismissed = true }, show = true) {}
+        }
+        // Timeout longer than default confirmation duration
+        rule.mainClock.advanceTimeBy(OpenOnPhoneDialogDefaults.DurationMillis + 1000)
+        assert(dismissed)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    @Test
+    fun openOnPhone_correct_colors() {
+        rule.mainClock.autoAdvance = false
+        var expectedIconColor: Color = Color.Unspecified
+        var expectedIconContainerColor: Color = Color.Unspecified
+        var expectedProgressIndicatorColor: Color = Color.Unspecified
+        var expectedProgressTrackColor: Color = Color.Unspecified
+        rule.setContentWithTheme {
+            OpenOnPhoneDialog(
+                onDismissRequest = {},
+                modifier = Modifier.testTag(TEST_TAG),
+                show = true
+            )
+            expectedIconColor = MaterialTheme.colorScheme.primary
+            expectedIconContainerColor = MaterialTheme.colorScheme.primaryContainer
+            expectedProgressIndicatorColor = MaterialTheme.colorScheme.primary
+            expectedProgressTrackColor = MaterialTheme.colorScheme.onPrimary
+        }
+        // Advance time by half of the default confirmation duration, so that the track and
+        // indicator are shown
+        rule.mainClock.advanceTimeBy(OpenOnPhoneDialogDefaults.DurationMillis / 2)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedIconColor)
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertContainsColor(expectedIconContainerColor)
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertContainsColor(expectedProgressIndicatorColor)
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertContainsColor(expectedProgressTrackColor)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    @Test
+    fun openOnPhone_custom_colors() {
+        rule.mainClock.autoAdvance = false
+        val customIconColor: Color = Color.Red
+        val customIconContainerColor: Color = Color.Green
+        val customProgressIndicatorColor: Color = Color.Blue
+        val customProgressTrackColor: Color = Color.Magenta
+        rule.setContentWithTheme {
+            OpenOnPhoneDialog(
+                onDismissRequest = {},
+                modifier = Modifier.testTag(TEST_TAG),
+                colors =
+                    OpenOnPhoneDialogDefaults.colors(
+                        iconColor = customIconColor,
+                        iconContainerColor = customIconContainerColor,
+                        progressIndicatorColor = customProgressIndicatorColor,
+                        progressTrackColor = customProgressTrackColor
+                    ),
+                show = true
+            )
+        }
+        // Advance time by half of the default confirmation duration, so that the track and
+        // indicator are shown
+        rule.mainClock.advanceTimeBy(OpenOnPhoneDialogDefaults.DurationMillis / 2)
+
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconColor)
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconContainerColor)
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertContainsColor(customProgressIndicatorColor)
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customProgressTrackColor)
+    }
+}
+
+private const val IconTestTag = "icon"
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
index 3b9584c..166a54c 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
@@ -611,7 +611,7 @@
     private fun ComposeContentTestRule.verifyTextToggleButtonColors(
         status: Status,
         checked: Boolean,
-        colors: @Composable () -> ToggleButtonColors,
+        colors: @Composable () -> TextToggleButtonColors,
         containerColor: @Composable () -> Color,
         contentColor: @Composable () -> Color,
     ) {
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/AlertDialog.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AlertDialog.kt
similarity index 92%
rename from wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/AlertDialog.kt
rename to wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AlertDialog.kt
index 2b34a71..f0cc000 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/AlertDialog.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AlertDialog.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.wear.compose.material3.dialog
+package androidx.wear.compose.material3
 
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
@@ -46,25 +46,12 @@
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
 import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
-import androidx.wear.compose.material3.Button
-import androidx.wear.compose.material3.ButtonDefaults
-import androidx.wear.compose.material3.EdgeButton
-import androidx.wear.compose.material3.FilledIconButton
-import androidx.wear.compose.material3.FilledTonalIconButton
-import androidx.wear.compose.material3.Icon
-import androidx.wear.compose.material3.LocalContentColor
-import androidx.wear.compose.material3.LocalTextAlign
-import androidx.wear.compose.material3.LocalTextMaxLines
-import androidx.wear.compose.material3.LocalTextStyle
-import androidx.wear.compose.material3.MaterialTheme
-import androidx.wear.compose.material3.PaddingDefaults
-import androidx.wear.compose.material3.ScreenScaffold
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.bottomSpacing
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.contentTopSpacing
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.iconBottomSpacing
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.textMessageTopSpacing
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.textPaddingFraction
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.titlePaddingFraction
+import androidx.wear.compose.material3.AlertDialogDefaults.bottomSpacing
+import androidx.wear.compose.material3.AlertDialogDefaults.contentTopSpacing
+import androidx.wear.compose.material3.AlertDialogDefaults.iconBottomSpacing
+import androidx.wear.compose.material3.AlertDialogDefaults.textMessageTopSpacing
+import androidx.wear.compose.material3.AlertDialogDefaults.textPaddingFraction
+import androidx.wear.compose.material3.AlertDialogDefaults.titlePaddingFraction
 import androidx.wear.compose.materialcore.isSmallScreen
 import androidx.wear.compose.materialcore.screenWidthDp
 
@@ -79,7 +66,7 @@
  *
  * Example of an [AlertDialog] with an icon, title and two buttons to confirm and dismiss:
  *
- * @sample androidx.wear.compose.material3.samples.dialog.AlertDialogWithConfirmAndDismissSample
+ * @sample androidx.wear.compose.material3.samples.AlertDialogWithConfirmAndDismissSample
  * @param show A boolean indicating whether the dialog should be displayed.
  * @param onDismissRequest A lambda function to be called when the dialog is dismissed by swiping
  *   right (typically also called by the [dismissButton]).
@@ -146,11 +133,11 @@
  *
  * Example of an [AlertDialog] with an icon, title, text and bottom [EdgeButton]:
  *
- * @sample androidx.wear.compose.material3.samples.dialog.AlertDialogWithBottomButtonSample
+ * @sample androidx.wear.compose.material3.samples.AlertDialogWithBottomButtonSample
  *
  * Example of an [AlertDialog] with content groups and a bottom [EdgeButton]:
  *
- * @sample androidx.wear.compose.material3.samples.dialog.AlertDialogWithContentGroupsSample
+ * @sample androidx.wear.compose.material3.samples.AlertDialogWithContentGroupsSample
  * @param show A boolean indicating whether the dialog should be displayed.
  * @param onDismissRequest A lambda function to be called when the dialog is dismissed by swiping to
  *   the right or by other dismiss action.
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
index e6e84b1..ec6ee95a 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
@@ -220,7 +220,7 @@
     internal var defaultOutlinedIconButtonColorsCached: IconButtonColors? = null
 
     // Icon Toggle Button
-    internal var defaultIconToggleButtonColorsCached: ToggleButtonColors? = null
+    internal var defaultIconToggleButtonColorsCached: IconToggleButtonColors? = null
 
     // Text Button
     internal var defaultTextButtonColorsCached: TextButtonColors? = null
@@ -230,7 +230,7 @@
     internal var defaultOutlinedTextButtonColorsCached: TextButtonColors? = null
 
     // Text Toggle Button
-    internal var defaultTextToggleButtonColorsCached: ToggleButtonColors? = null
+    internal var defaultTextToggleButtonColorsCached: TextToggleButtonColors? = null
 
     // Card
     internal var defaultCardColorsCached: CardColors? = null
@@ -254,6 +254,14 @@
     // Level Indicator
     internal var defaultLevelIndicatorColorsCached: LevelIndicatorColors? = null
 
+    // Confirmation
+    internal var defaultConfirmationColorsCached: ConfirmationColors? = null
+    internal var defaultSuccessConfirmationColorsCached: ConfirmationColors? = null
+    internal var defaultFailureConfirmationColorsCached: ConfirmationColors? = null
+
+    // Open on Phone dialog
+    internal var mDefaultOpenOnPhoneDialogColorsCached: OpenOnPhoneDialogColors? = null
+
     // Picker
     internal var defaultTimePickerColorsCached: TimePickerColors? = null
     internal var defaultDatePickerColorsCached: DatePickerColors? = null
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Confirmation.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Confirmation.kt
new file mode 100644
index 0000000..e88a30b
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Confirmation.kt
@@ -0,0 +1,656 @@
+/*
+ * Copyright 2024 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.wear.compose.material3
+
+import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
+import androidx.compose.animation.graphics.res.animatedVectorResource
+import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Spacer
+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.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+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.graphics.graphicsLayer
+import androidx.compose.ui.platform.LocalAccessibilityManager
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.DialogProperties
+import androidx.wear.compose.foundation.CurvedDirection
+import androidx.wear.compose.foundation.CurvedLayout
+import androidx.wear.compose.foundation.CurvedModifier
+import androidx.wear.compose.foundation.CurvedScope
+import androidx.wear.compose.foundation.CurvedTextStyle
+import androidx.wear.compose.foundation.padding
+import androidx.wear.compose.material3.tokens.ColorSchemeKeyTokens
+import androidx.wear.compose.materialcore.screenHeightDp
+import androidx.wear.compose.materialcore.screenWidthDp
+import kotlinx.coroutines.delay
+
+/**
+ * Shows a [Confirmation] dialog with an icon and optional very short curved text. The length of the
+ * curved text should be very short and should not exceed 1-2 words. If a longer text required, then
+ * another [Confirmation] overload with a column content should be used instead.
+ *
+ * The confirmation will be showing a message to the user for [durationMillis]. After a specified
+ * timeout, the [onDismissRequest] callback will be invoked, where it's up to the caller to handle
+ * the dismissal. To hide the confirmation, [show] parameter should be set to false.
+ *
+ * Example of a [Confirmation] with an icon and a curved text content:
+ *
+ * @sample androidx.wear.compose.material3.samples.ConfirmationSample
+ * @param show A boolean indicating whether the confirmation should be displayed.
+ * @param onDismissRequest A lambda function to be called when the dialog is dismissed - either by
+ *   swiping right or when the [durationMillis] has passed.
+ * @param curvedText A slot for displaying curved text content which will be shown along the bottom
+ *   edge of the dialog.
+ * @param modifier Modifier to be applied to the confirmation content.
+ * @param colors A [ConfirmationColors] object for customizing the colors used in this
+ *   [Confirmation].
+ * @param properties An optional [DialogProperties] object for configuring the dialog's behavior.
+ * @param durationMillis The duration in milliseconds for which the dialog is displayed. Defaults to
+ *   [ConfirmationDefaults.ConfirmationDurationMillis].
+ * @param content A slot for displaying an icon inside the confirmation dialog. It's recommended to
+ *   set its size to [ConfirmationDefaults.IconSize]
+ */
+@Composable
+fun Confirmation(
+    show: Boolean,
+    onDismissRequest: () -> Unit,
+    curvedText: (CurvedScope.() -> Unit)?,
+    modifier: Modifier = Modifier,
+    colors: ConfirmationColors = ConfirmationDefaults.confirmationColors(),
+    properties: DialogProperties = DialogProperties(),
+    durationMillis: Long = ConfirmationDefaults.ConfirmationDurationMillis,
+    content: @Composable BoxScope.() -> Unit
+) {
+    ConfirmationImpl(
+        show = show,
+        onDismissRequest = onDismissRequest,
+        modifier = modifier,
+        iconContainer = confirmationIconContainer(true, colors.iconContainerColor),
+        curvedText = curvedText,
+        colors = colors,
+        properties = properties,
+        durationMillis = durationMillis,
+        content = content
+    )
+}
+
+/**
+ * Shows a [Confirmation] dialog with an icon and optional short text. The length of the text should
+ * not exceed 3 lines. If the text is very short and fits into 1-2 words, consider using another
+ * [Confirmation] overload with curvedContent instead.
+ *
+ * The confirmation will show a message to the user for [durationMillis]. After a specified timeout,
+ * the [onDismissRequest] callback will be invoked, where it's up to the caller to handle the
+ * dismissal. To hide the confirmation, [show] parameter should be set to false.
+ *
+ * Example of a [Confirmation] with an icon and a text which fits into 3 lines:
+ *
+ * @sample androidx.wear.compose.material3.samples.LongTextConfirmationSample
+ * @param show A boolean indicating whether the confirmation should be displayed.
+ * @param onDismissRequest A lambda function to be called when the dialog is dismissed - either by
+ *   swiping right or when the [durationMillis] has passed.
+ * @param text A slot for displaying text below the icon. It should not exceed 3 lines.
+ * @param modifier Modifier to be applied to the confirmation content.
+ * @param colors A [ConfirmationColors] object for customizing the colors used in this
+ *   [Confirmation].
+ * @param properties An optional [DialogProperties] object for configuring the dialog's behavior.
+ * @param durationMillis The duration in milliseconds for which the dialog is displayed. Defaults to
+ *   [ConfirmationDefaults.ConfirmationDurationMillis].
+ * @param content A slot for displaying an icon inside the confirmation dialog, which can be
+ *   animated. It's recommended to set its size to [ConfirmationDefaults.SmallIconSize]
+ */
+@Composable
+fun Confirmation(
+    show: Boolean,
+    onDismissRequest: () -> Unit,
+    text: @Composable (ColumnScope.() -> Unit)?,
+    modifier: Modifier = Modifier,
+    colors: ConfirmationColors = ConfirmationDefaults.confirmationColors(),
+    properties: DialogProperties = DialogProperties(),
+    durationMillis: Long = ConfirmationDefaults.ConfirmationDurationMillis,
+    content: @Composable BoxScope.() -> Unit
+) {
+
+    val a11yDurationMillis =
+        LocalAccessibilityManager.current?.calculateRecommendedTimeoutMillis(
+            originalTimeoutMillis = durationMillis,
+            containsIcons = true,
+            containsText = text != null,
+            containsControls = false,
+        ) ?: durationMillis
+
+    LaunchedEffect(show, a11yDurationMillis) {
+        if (show) {
+            delay(a11yDurationMillis)
+            onDismissRequest()
+        }
+    }
+
+    Dialog(
+        showDialog = show,
+        modifier = modifier,
+        onDismissRequest = onDismissRequest,
+        properties = properties,
+    ) {
+        Box(Modifier.fillMaxSize()) {
+            val horizontalPadding =
+                screenWidthDp().dp * ConfirmationDefaults.HorizontalLinearContentPaddingFraction
+            Column(
+                modifier = Modifier.align(Alignment.Center).padding(horizontal = horizontalPadding),
+                horizontalAlignment = Alignment.CenterHorizontally
+            ) {
+                Box(
+                    modifier = Modifier.align(Alignment.CenterHorizontally),
+                    contentAlignment = Alignment.Center
+                ) {
+                    confirmationIconContainer(false, colors.iconContainerColor)()
+                    CompositionLocalProvider(LocalContentColor provides colors.iconColor) {
+                        content()
+                    }
+                }
+                CompositionLocalProvider(
+                    LocalContentColor provides colors.textColor,
+                    LocalTextStyle provides MaterialTheme.typography.titleMedium,
+                    LocalTextAlign provides TextAlign.Center,
+                    LocalTextMaxLines provides ConfirmationDefaults.LinearContentMaxLines
+                ) {
+                    if (text != null) {
+                        Spacer(Modifier.height(ConfirmationDefaults.LinearContentSpacing))
+                        text()
+                        Spacer(Modifier.height(ConfirmationDefaults.LinearContentSpacing))
+                    }
+                }
+            }
+        }
+    }
+}
+
+/**
+ * Shows a [Confirmation] dialog with a success icon and optional short curved text. This
+ * confirmation indicates a successful operation or action.
+ *
+ * The confirmation will show a message to the user for [durationMillis]. After a specified timeout,
+ * the [onDismissRequest] callback will be invoked, where it's up to the caller to handle the
+ * dismissal. To hide the confirmation, [show] parameter should be set to false.
+ *
+ * Example of a [SuccessConfirmation] usage:
+ *
+ * @sample androidx.wear.compose.material3.samples.SuccessConfirmationSample
+ * @param show A boolean indicating whether the confirmation should be displayed.
+ * @param onDismissRequest A lambda function to be called when the dialog is dismissed - either by
+ *   swiping right or when the [durationMillis] has passed.
+ * @param modifier Modifier to be applied to the confirmation content.
+ * @param curvedText A slot for displaying curved text content which will be shown along the bottom
+ *   edge of the dialog. Defaults to a localized success message.
+ * @param colors A [ConfirmationColors] object for customizing the colors used in this
+ *   [SuccessConfirmation].
+ * @param properties An optional [DialogProperties] object for configuring the dialog's behavior.
+ * @param durationMillis The duration in milliseconds for which the dialog is displayed. Defaults to
+ *   [ConfirmationDefaults.ConfirmationDurationMillis].
+ * @param content A slot for displaying an icon inside the confirmation dialog, which can be
+ *   animated. Defaults to an animated [ConfirmationDefaults.SuccessIcon].
+ */
+@Composable
+fun SuccessConfirmation(
+    show: Boolean,
+    onDismissRequest: () -> Unit,
+    modifier: Modifier = Modifier,
+    curvedText: (CurvedScope.() -> Unit)? = ConfirmationDefaults.successText(),
+    colors: ConfirmationColors = ConfirmationDefaults.successColors(),
+    properties: DialogProperties = DialogProperties(),
+    durationMillis: Long = ConfirmationDefaults.ConfirmationDurationMillis,
+    content: @Composable BoxScope.() -> Unit = ConfirmationDefaults.SuccessIcon,
+) {
+    ConfirmationImpl(
+        show = show,
+        onDismissRequest = onDismissRequest,
+        modifier = modifier,
+        content = content,
+        iconContainer = successIconContainer(colors.iconContainerColor),
+        curvedText = curvedText,
+        colors = colors,
+        properties = properties,
+        durationMillis = durationMillis
+    )
+}
+
+/**
+ * Shows a [Confirmation] dialog with a failure icon and an optional short curved text. This
+ * confirmation indicates an unsuccessful operation or action.
+ *
+ * The confirmation will show a message to the user for [durationMillis]. After a specified timeout,
+ * the [onDismissRequest] callback will be invoked, where it's up to the caller to handle the
+ * dismissal. To hide the confirmation, [show] parameter should be set to false.
+ *
+ * Example of a [FailureConfirmation] usage:
+ *
+ * @sample androidx.wear.compose.material3.samples.FailureConfirmationSample
+ * @param show A boolean indicating whether the confirmation should be displayed.
+ * @param onDismissRequest A lambda function to be called when the dialog is dismissed - either by
+ *   swiping right or when the [durationMillis] has passed.
+ * @param modifier Modifier to be applied to the confirmation content.
+ * @param curvedText A slot for displaying curved text content which will be shown along the bottom
+ *   edge of the dialog. Defaults to a localized failure message.
+ * @param colors A [ConfirmationColors] object for customizing the colors used in this
+ *   [FailureConfirmation].
+ * @param properties An optional [DialogProperties] object for configuring the dialog's behavior.
+ * @param durationMillis The duration in milliseconds for which the dialog is displayed. Defaults to
+ *   [ConfirmationDefaults.ConfirmationDurationMillis].
+ * @param content A slot for displaying an icon inside the confirmation dialog, which can be
+ *   animated. Defaults to [ConfirmationDefaults.FailureIcon].
+ */
+@Composable
+fun FailureConfirmation(
+    show: Boolean,
+    onDismissRequest: () -> Unit,
+    modifier: Modifier = Modifier,
+    curvedText: (CurvedScope.() -> Unit)? = ConfirmationDefaults.failureText(),
+    colors: ConfirmationColors = ConfirmationDefaults.failureColors(),
+    properties: DialogProperties = DialogProperties(),
+    durationMillis: Long = ConfirmationDefaults.ConfirmationDurationMillis,
+    content: @Composable BoxScope.() -> Unit = ConfirmationDefaults.FailureIcon,
+) {
+    ConfirmationImpl(
+        show = show,
+        onDismissRequest = onDismissRequest,
+        modifier = modifier,
+        iconContainer = failureIconContainer(colors.iconContainerColor),
+        curvedText = curvedText,
+        colors = colors,
+        properties = properties,
+        durationMillis = durationMillis,
+        content = content
+    )
+}
+
+/** Contains default values used by [Confirmation] composable. */
+object ConfirmationDefaults {
+
+    /**
+     * Returns a lambda to display a curved success message. The success message is retrieved from
+     * the application's string resources.
+     */
+    @Composable
+    fun successText(): CurvedScope.() -> Unit =
+        curvedText(
+            LocalContext.current.resources.getString(R.string.wear_m3c_confirmation_success_message)
+        )
+
+    /**
+     * Returns a lambda to display a curved failure message. The failure message is retrieved from
+     * the application's string resources.
+     */
+    @Composable
+    fun failureText(): CurvedScope.() -> Unit =
+        curvedText(
+            LocalContext.current.resources.getString(R.string.wear_m3c_confirmation_failure_message)
+        )
+
+    /**
+     * A default composable used in [SuccessConfirmation] that displays a success icon with an
+     * animation.
+     */
+    @OptIn(ExperimentalAnimationGraphicsApi::class)
+    val SuccessIcon: @Composable BoxScope.() -> Unit = {
+        val animation = AnimatedImageVector.animatedVectorResource(R.drawable.check_animation)
+        var atEnd by remember { mutableStateOf(false) }
+        LaunchedEffect(Unit) {
+            delay(FailureIconDelay)
+            atEnd = true
+        }
+        Icon(
+            painter = rememberAnimatedVectorPainter(animation, atEnd),
+            contentDescription = null,
+            modifier = Modifier.size(IconSize)
+        )
+    }
+
+    /**
+     * A default composable used in [FailureConfirmation] that displays a failure icon with an
+     * animation.
+     */
+    @OptIn(ExperimentalAnimationGraphicsApi::class)
+    val FailureIcon: @Composable BoxScope.() -> Unit = {
+        val animation = AnimatedImageVector.animatedVectorResource(R.drawable.failure_animation)
+        var atEnd by remember { mutableStateOf(false) }
+        LaunchedEffect(Unit) {
+            delay(FailureIconDelay)
+            atEnd = true
+        }
+        Icon(
+            painter = rememberAnimatedVectorPainter(animation, atEnd),
+            contentDescription = null,
+            modifier = Modifier.size(IconSize)
+        )
+    }
+
+    /**
+     * A default composable that displays text along a curved path, used in [Confirmation].
+     *
+     * @param text The text to display.
+     * @param style The style to apply to the text. Defaults to
+     *   CurvedTextStyle(MaterialTheme.typography.titleLarge).
+     */
+    @Composable
+    fun curvedText(
+        text: String,
+        style: CurvedTextStyle = CurvedTextStyle(MaterialTheme.typography.titleLarge)
+    ): CurvedScope.() -> Unit = {
+        curvedText(
+            text = text,
+            style = style,
+            maxSweepAngle = CurvedTextDefaults.StaticContentMaxSweepAngle,
+            modifier = CurvedModifier.padding(PaddingDefaults.edgePadding),
+            angularDirection = CurvedDirection.Angular.Reversed
+        )
+    }
+
+    /**
+     * Creates a [ConfirmationColors] that represents the default colors used in a [Confirmation].
+     */
+    @Composable fun confirmationColors() = MaterialTheme.colorScheme.defaultConfirmationColors
+
+    /**
+     * Creates a [ConfirmationColors] with modified colors used in [Confirmation].
+     *
+     * @param iconColor The icon color.
+     * @param iconContainerColor The icon container color.
+     * @param textColor The text color.
+     */
+    @Composable
+    fun confirmationColors(
+        iconColor: Color = Color.Unspecified,
+        iconContainerColor: Color = Color.Unspecified,
+        textColor: Color = Color.Unspecified,
+    ) =
+        MaterialTheme.colorScheme.defaultConfirmationColors.copy(
+            iconColor = iconColor,
+            iconContainerColor = iconContainerColor,
+            textColor = textColor,
+        )
+
+    /**
+     * Creates a [ConfirmationColors] that represents the default colors used in a
+     * [SuccessConfirmation].
+     */
+    @Composable fun successColors() = MaterialTheme.colorScheme.defaultSuccessConfirmationColors
+
+    /**
+     * Creates a [ConfirmationColors] with modified colors used in [SuccessConfirmation].
+     *
+     * @param iconColor The icon color.
+     * @param iconContainerColor The icon container color.
+     * @param textColor The text color.
+     */
+    @Composable
+    fun successColors(
+        iconColor: Color = Color.Unspecified,
+        iconContainerColor: Color = Color.Unspecified,
+        textColor: Color = Color.Unspecified,
+    ) =
+        MaterialTheme.colorScheme.defaultSuccessConfirmationColors.copy(
+            iconColor = iconColor,
+            iconContainerColor = iconContainerColor,
+            textColor = textColor,
+        )
+
+    /**
+     * Creates a [ConfirmationColors] that represents the default colors used in a
+     * [FailureConfirmation].
+     */
+    @Composable fun failureColors() = MaterialTheme.colorScheme.defaultFailureConfirmationColors
+
+    /**
+     * Creates a [ConfirmationColors] with modified colors used in [FailureConfirmation].
+     *
+     * @param iconColor The icon color.
+     * @param iconContainerColor The icon container color.
+     * @param textColor The text color.
+     */
+    @Composable
+    fun failureColors(
+        iconColor: Color = Color.Unspecified,
+        iconContainerColor: Color = Color.Unspecified,
+        textColor: Color = Color.Unspecified,
+    ) =
+        MaterialTheme.colorScheme.defaultFailureConfirmationColors.copy(
+            iconColor = iconColor,
+            iconContainerColor = iconContainerColor,
+            textColor = textColor,
+        )
+
+    /** Default timeout for the [Confirmation] dialog, in milliseconds. */
+    const val ConfirmationDurationMillis = 4000L
+
+    /** Default icon size for the [Confirmation] with curved content */
+    val IconSize = 52.dp
+
+    /** Default icon size for the [Confirmation] with linear content */
+    val SmallIconSize = 36.dp
+
+    private val ColorScheme.defaultConfirmationColors: ConfirmationColors
+        get() {
+            return defaultConfirmationColorsCached
+                ?: ConfirmationColors(
+                        iconColor = fromToken(ColorSchemeKeyTokens.Primary),
+                        iconContainerColor = fromToken(ColorSchemeKeyTokens.OnPrimary),
+                        textColor = fromToken(ColorSchemeKeyTokens.OnBackground)
+                    )
+                    .also { defaultConfirmationColorsCached = it }
+        }
+
+    private val ColorScheme.defaultSuccessConfirmationColors: ConfirmationColors
+        get() {
+            return defaultSuccessConfirmationColorsCached
+                ?: ConfirmationColors(
+                        iconColor = fromToken(ColorSchemeKeyTokens.Primary),
+                        iconContainerColor = fromToken(ColorSchemeKeyTokens.OnPrimary),
+                        textColor = fromToken(ColorSchemeKeyTokens.OnBackground)
+                    )
+                    .also { defaultSuccessConfirmationColorsCached = it }
+        }
+
+    private val ColorScheme.defaultFailureConfirmationColors: ConfirmationColors
+        get() {
+            return defaultFailureConfirmationColorsCached
+                ?: ConfirmationColors(
+                        iconColor = fromToken(ColorSchemeKeyTokens.ErrorContainer),
+                        iconContainerColor =
+                            fromToken(ColorSchemeKeyTokens.OnErrorContainer).copy(.8f),
+                        textColor = fromToken(ColorSchemeKeyTokens.OnBackground)
+                    )
+                    .also { defaultFailureConfirmationColorsCached = it }
+        }
+
+    internal val FailureIconDelay = 67L
+
+    internal val SuccessWidthFraction = 0.496f
+    internal val SuccessHeightFraction = 0.6f
+    internal val FailureSizeFraction = 0.52f
+
+    internal val ConfirmationIconContainerSmallSize = 80.dp
+    internal val ConfirmationIconContainerSizeFraction = 0.52
+
+    internal val ExtraBottomPaddingFraction = 0.02f
+
+    internal val LinearContentSpacing = 8.dp
+    internal val LinearContentMaxLines = 3
+    internal val HorizontalLinearContentPaddingFraction = 0.12f
+}
+
+/**
+ * Represents the colors used in [Confirmation], [SuccessConfirmation] and [FailureConfirmation].
+ *
+ * @param iconColor Color used to tint the icon.
+ * @param iconContainerColor The color of the container behind the icon.
+ * @param textColor Color used to tint the text.
+ */
+class ConfirmationColors(
+    val iconColor: Color,
+    val iconContainerColor: Color,
+    val textColor: Color,
+) {
+    internal fun copy(
+        iconColor: Color? = null,
+        iconContainerColor: Color? = null,
+        textColor: Color? = null
+    ) =
+        ConfirmationColors(
+            iconColor = iconColor ?: this.iconColor,
+            iconContainerColor = iconContainerColor ?: this.iconContainerColor,
+            textColor = textColor ?: this.textColor,
+        )
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || other !is ConfirmationColors) return false
+
+        if (iconColor != other.iconColor) return false
+        if (iconContainerColor != other.iconContainerColor) return false
+        if (textColor != other.textColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = iconColor.hashCode()
+        result = 31 * result + iconContainerColor.hashCode()
+        result = 31 * result + textColor.hashCode()
+        return result
+    }
+}
+
+@Composable
+internal fun ConfirmationImpl(
+    show: Boolean,
+    onDismissRequest: () -> Unit,
+    modifier: Modifier,
+    iconContainer: @Composable BoxScope.() -> Unit,
+    curvedText: (CurvedScope.() -> Unit)?,
+    colors: ConfirmationColors,
+    properties: DialogProperties,
+    durationMillis: Long,
+    content: @Composable BoxScope.() -> Unit
+) {
+    val a11yDurationMillis =
+        LocalAccessibilityManager.current?.calculateRecommendedTimeoutMillis(
+            originalTimeoutMillis = durationMillis,
+            containsIcons = true,
+            containsText = curvedText != null,
+            containsControls = false,
+        ) ?: durationMillis
+
+    LaunchedEffect(show, a11yDurationMillis) {
+        if (show) {
+            delay(a11yDurationMillis)
+            onDismissRequest()
+        }
+    }
+
+    Dialog(
+        showDialog = show,
+        modifier = modifier,
+        onDismissRequest = onDismissRequest,
+        properties = properties,
+    ) {
+        Box(modifier = Modifier.fillMaxSize()) {
+            val bottomPadding =
+                if (curvedText != null)
+                    screenHeightDp() * ConfirmationDefaults.ExtraBottomPaddingFraction
+                else 0f
+            Box(
+                Modifier.fillMaxSize().padding(bottom = bottomPadding.dp),
+                contentAlignment = Alignment.Center
+            ) {
+                iconContainer()
+                CompositionLocalProvider(LocalContentColor provides colors.iconColor) { content() }
+            }
+            CompositionLocalProvider(LocalContentColor provides colors.textColor) {
+                curvedText?.let { CurvedLayout(anchor = 90f, contentBuilder = curvedText) }
+            }
+        }
+    }
+}
+
+private fun confirmationIconContainer(
+    curvedContent: Boolean,
+    color: Color
+): @Composable BoxScope.() -> Unit = {
+    val iconShape =
+        if (curvedContent) MaterialTheme.shapes.extraLarge else MaterialTheme.shapes.large
+    val width =
+        if (curvedContent) {
+            (screenWidthDp() * ConfirmationDefaults.ConfirmationIconContainerSizeFraction).dp
+        } else ConfirmationDefaults.ConfirmationIconContainerSmallSize
+
+    Box(
+        Modifier.size(width)
+            .graphicsLayer {
+                shape = iconShape
+                clip = true
+            }
+            .background(color)
+            .align(Alignment.Center)
+    )
+}
+
+private fun successIconContainer(color: Color): @Composable BoxScope.() -> Unit = {
+    val width = screenWidthDp() * ConfirmationDefaults.SuccessWidthFraction
+    val height = screenWidthDp() * ConfirmationDefaults.SuccessHeightFraction
+    Box(
+        Modifier.size(width.dp, height.dp)
+            .graphicsLayer {
+                rotationZ = 45f
+                shape = CircleShape
+                clip = true
+            }
+            .background(color)
+    )
+}
+
+private fun failureIconContainer(color: Color): @Composable BoxScope.() -> Unit = {
+    val iconShape = MaterialTheme.shapes.extraLarge
+    val width = screenWidthDp() * ConfirmationDefaults.FailureSizeFraction
+    Box(
+        Modifier.size(width.dp)
+            .graphicsLayer {
+                shape = iconShape
+                clip = true
+            }
+            .background(color)
+    )
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/Dialog.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Dialog.kt
similarity index 97%
rename from wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/Dialog.kt
rename to wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Dialog.kt
index d42ba2d..f5c6dc6 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/Dialog.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Dialog.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.wear.compose.material3.dialog
+package androidx.wear.compose.material3
 
 import androidx.compose.animation.core.MutableTransitionState
 import androidx.compose.animation.core.Transition
@@ -34,9 +34,6 @@
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.window.DialogProperties
 import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
-import androidx.wear.compose.material3.MaterialTheme
-import androidx.wear.compose.material3.ScreenScaffold
-import androidx.wear.compose.material3.SwipeToDismissBox
 import androidx.wear.compose.material3.tokens.MotionTokens
 
 /**
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
index 7f89966..1d4e8e4 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.tween
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.InteractionSource
@@ -27,6 +29,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
@@ -37,8 +40,10 @@
 import androidx.wear.compose.material3.tokens.FilledTonalIconButtonTokens
 import androidx.wear.compose.material3.tokens.IconButtonTokens
 import androidx.wear.compose.material3.tokens.IconToggleButtonTokens
+import androidx.wear.compose.material3.tokens.MotionTokens
 import androidx.wear.compose.material3.tokens.OutlinedIconButtonTokens
 import androidx.wear.compose.material3.tokens.ShapeTokens
+import androidx.wear.compose.materialcore.animateSelectionColor
 
 /**
  * Wear Material [IconButton] is a circular, icon-only button with transparent background and no
@@ -355,8 +360,8 @@
  * @param modifier Modifier to be applied to the toggle button.
  * @param enabled Controls the enabled state of the toggle button. When `false`, this toggle button
  *   will not be clickable.
- * @param colors [ToggleButtonColors] that will be used to resolve the container and content color
- *   for this toggle button.
+ * @param colors [IconToggleButtonColors] that will be used to resolve the container and content
+ *   color for this toggle button.
  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
  *   emitting [Interaction]s for this button. You can use this to change the button's appearance or
  *   preview the button in different states. Note that if `null` is provided, interactions will
@@ -372,7 +377,7 @@
     onCheckedChange: (Boolean) -> Unit,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
-    colors: ToggleButtonColors = IconButtonDefaults.iconToggleButtonColors(),
+    colors: IconToggleButtonColors = IconButtonDefaults.iconToggleButtonColors(),
     interactionSource: MutableInteractionSource? = null,
     shape: Shape = IconButtonDefaults.shape,
     border: BorderStroke? = null,
@@ -606,7 +611,7 @@
         )
 
     /**
-     * Creates a [ToggleButtonColors] for a [IconToggleButton]
+     * Creates an [IconToggleButtonColors] for a [IconToggleButton]
      * - by default, a colored background with a contrasting content color.
      *
      * If the button is disabled, then the colors will have an alpha ([DisabledContentAlpha] and
@@ -616,7 +621,7 @@
     fun iconToggleButtonColors() = MaterialTheme.colorScheme.defaultIconToggleButtonColors
 
     /**
-     * Creates a [ToggleButtonColors] for a [IconToggleButton]
+     * Creates a [IconToggleButtonColors] for a [IconToggleButton]
      * - by default, a colored background with a contrasting content color.
      *
      * If the button is disabled, then the colors will have an alpha ([DisabledContentAlpha] and
@@ -649,7 +654,7 @@
         disabledCheckedContentColor: Color = Color.Unspecified,
         disabledUncheckedContainerColor: Color = Color.Unspecified,
         disabledUncheckedContentColor: Color = Color.Unspecified,
-    ): ToggleButtonColors =
+    ): IconToggleButtonColors =
         MaterialTheme.colorScheme.defaultIconToggleButtonColors.copy(
             checkedContainerColor = checkedContainerColor,
             checkedContentColor = checkedContentColor,
@@ -797,10 +802,10 @@
                     .also { defaultIconButtonColorsCached = it }
         }
 
-    private val ColorScheme.defaultIconToggleButtonColors: ToggleButtonColors
+    private val ColorScheme.defaultIconToggleButtonColors: IconToggleButtonColors
         get() {
             return defaultIconToggleButtonColorsCached
-                ?: ToggleButtonColors(
+                ?: IconToggleButtonColors(
                         checkedContainerColor =
                             fromToken(IconToggleButtonTokens.CheckedContainerColor),
                         checkedContentColor = fromToken(IconToggleButtonTokens.CheckedContentColor),
@@ -914,3 +919,129 @@
         return result
     }
 }
+
+/**
+ * Represents the different container and content colors used for [IconToggleButton] in various
+ * states, that are checked, unchecked, enabled and disabled.
+ *
+ * @param checkedContainerColor Container or background color when the toggle button is checked
+ * @param checkedContentColor Color of the content (text or icon) when the toggle button is checked
+ * @param uncheckedContainerColor Container or background color when the toggle button is unchecked
+ * @param uncheckedContentColor Color of the content (text or icon) when the toggle button is
+ *   unchecked
+ * @param disabledCheckedContainerColor Container or background color when the toggle button is
+ *   disabled and checked
+ * @param disabledCheckedContentColor Color of content (text or icon) when the toggle button is
+ *   disabled and checked
+ * @param disabledUncheckedContainerColor Container or background color when the toggle button is
+ *   disabled and unchecked
+ * @param disabledUncheckedContentColor Color of the content (text or icon) when the toggle button
+ *   is disabled and unchecked
+ */
+@Immutable
+class IconToggleButtonColors(
+    val checkedContainerColor: Color,
+    val checkedContentColor: Color,
+    val uncheckedContainerColor: Color,
+    val uncheckedContentColor: Color,
+    val disabledCheckedContainerColor: Color,
+    val disabledCheckedContentColor: Color,
+    val disabledUncheckedContainerColor: Color,
+    val disabledUncheckedContentColor: Color,
+) {
+    internal fun copy(
+        checkedContainerColor: Color,
+        checkedContentColor: Color,
+        uncheckedContainerColor: Color,
+        uncheckedContentColor: Color,
+        disabledCheckedContainerColor: Color,
+        disabledCheckedContentColor: Color,
+        disabledUncheckedContainerColor: Color,
+        disabledUncheckedContentColor: Color,
+    ): IconToggleButtonColors =
+        IconToggleButtonColors(
+            checkedContainerColor = checkedContainerColor.takeOrElse { this.checkedContainerColor },
+            checkedContentColor = checkedContentColor.takeOrElse { this.checkedContentColor },
+            uncheckedContainerColor =
+                uncheckedContainerColor.takeOrElse { this.uncheckedContainerColor },
+            uncheckedContentColor = uncheckedContentColor.takeOrElse { this.uncheckedContentColor },
+            disabledCheckedContainerColor =
+                disabledCheckedContainerColor.takeOrElse { this.disabledCheckedContainerColor },
+            disabledCheckedContentColor =
+                disabledCheckedContentColor.takeOrElse { this.disabledCheckedContentColor },
+            disabledUncheckedContainerColor =
+                disabledUncheckedContainerColor.takeOrElse { this.disabledUncheckedContainerColor },
+            disabledUncheckedContentColor =
+                disabledUncheckedContentColor.takeOrElse { this.disabledUncheckedContentColor },
+        )
+
+    /**
+     * Determines the container color based on whether the toggle button is [enabled] and [checked].
+     *
+     * @param enabled Whether the toggle button is enabled
+     * @param checked Whether the toggle button is checked
+     */
+    @Composable
+    internal fun containerColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedContainerColor,
+            uncheckedColor = uncheckedContainerColor,
+            disabledCheckedColor = disabledCheckedContainerColor,
+            disabledUncheckedColor = disabledUncheckedContainerColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
+
+    /**
+     * Determines the content color based on whether the toggle button is [enabled] and [checked].
+     *
+     * @param enabled Whether the toggle button is enabled
+     * @param checked Whether the toggle button is checked
+     */
+    @Composable
+    internal fun contentColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedContentColor,
+            uncheckedColor = uncheckedContentColor,
+            disabledCheckedColor = disabledCheckedContentColor,
+            disabledUncheckedColor = disabledUncheckedContentColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null) return false
+        if (this::class != other::class) return false
+
+        other as IconToggleButtonColors
+
+        if (checkedContainerColor != other.checkedContainerColor) return false
+        if (checkedContentColor != other.checkedContentColor) return false
+        if (uncheckedContainerColor != other.uncheckedContainerColor) return false
+        if (uncheckedContentColor != other.uncheckedContentColor) return false
+        if (disabledCheckedContainerColor != other.disabledCheckedContainerColor) return false
+        if (disabledCheckedContentColor != other.disabledCheckedContentColor) return false
+        if (disabledUncheckedContainerColor != other.disabledUncheckedContainerColor) return false
+        if (disabledUncheckedContentColor != other.disabledUncheckedContentColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = checkedContainerColor.hashCode()
+        result = 31 * result + checkedContentColor.hashCode()
+        result = 31 * result + uncheckedContainerColor.hashCode()
+        result = 31 * result + uncheckedContentColor.hashCode()
+        result = 31 * result + disabledCheckedContainerColor.hashCode()
+        result = 31 * result + disabledCheckedContentColor.hashCode()
+        result = 31 * result + disabledUncheckedContainerColor.hashCode()
+        result = 31 * result + disabledUncheckedContentColor.hashCode()
+        return result
+    }
+}
+
+private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
+    tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/OpenOnPhoneDialog.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/OpenOnPhoneDialog.kt
new file mode 100644
index 0000000..c7944ef
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/OpenOnPhoneDialog.kt
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2024 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.wear.compose.material3
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
+import androidx.compose.animation.graphics.res.animatedVectorResource
+import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+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.graphics.SolidColor
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.LocalAccessibilityManager
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.DialogProperties
+import androidx.wear.compose.foundation.CurvedDirection
+import androidx.wear.compose.foundation.CurvedLayout
+import androidx.wear.compose.foundation.CurvedModifier
+import androidx.wear.compose.foundation.CurvedScope
+import androidx.wear.compose.foundation.CurvedTextStyle
+import androidx.wear.compose.foundation.padding
+import androidx.wear.compose.material3.tokens.ColorSchemeKeyTokens
+import androidx.wear.compose.materialcore.screenHeightDp
+import androidx.wear.compose.materialcore.screenWidthDp
+import kotlinx.coroutines.delay
+
+/**
+ * A full-screen dialog that displays an animated icon with a curved text at the bottom.
+ *
+ * The dialog will be showing a message to the user for [durationMillis]. After a specified timeout,
+ * the [onDismissRequest] callback will be invoked, where it's up to the caller to handle the
+ * dismissal. To hide the dialog, [show] parameter should be set to false.
+ *
+ * This dialog is typically used to indicate that an action has been initiated and will continue on
+ * the user's phone. Once this dialog is displayed, it's developer responsibility to establish the
+ * connection between the watch and the phone.
+ *
+ * Example of an [OpenOnPhoneDialog] usage:
+ *
+ * @sample androidx.wear.compose.material3.samples.OpenOnPhoneDialogSample
+ * @param show A boolean indicating whether the dialog should be displayed.
+ * @param onDismissRequest A lambda function to be called when the dialog is dismissed - either by
+ *   swiping right or when the [durationMillis] has passed.
+ * @param modifier Modifier to be applied to the dialog content.
+ * @param curvedText A slot for displaying curved text content which will be shown along the bottom
+ *   edge of the dialog. Defaults to a localized open on phone message.
+ * @param colors [OpenOnPhoneDialogColors] that will be used to resolve the colors used for this
+ *   [OpenOnPhoneDialog].
+ * @param properties An optional [DialogProperties] object for configuring the dialog's behavior.
+ * @param durationMillis The duration in milliseconds for which the dialog is displayed. Defaults to
+ *   [OpenOnPhoneDialogDefaults.DurationMillis].
+ * @param content A slot for displaying an icon inside the open on phone dialog, which can be
+ *   animated. Defaults to [OpenOnPhoneDialogDefaults.Icon].
+ */
+@Composable
+fun OpenOnPhoneDialog(
+    show: Boolean,
+    onDismissRequest: () -> Unit,
+    modifier: Modifier = Modifier,
+    curvedText: (CurvedScope.() -> Unit)? = OpenOnPhoneDialogDefaults.curvedText(),
+    colors: OpenOnPhoneDialogColors = OpenOnPhoneDialogDefaults.colors(),
+    properties: DialogProperties = DialogProperties(),
+    durationMillis: Long = OpenOnPhoneDialogDefaults.DurationMillis,
+    content: @Composable BoxScope.() -> Unit = OpenOnPhoneDialogDefaults.Icon,
+) {
+    var progress by remember(show) { mutableFloatStateOf(0f) }
+    val animatable = remember { Animatable(0f) }
+
+    val a11yDurationMillis =
+        LocalAccessibilityManager.current?.calculateRecommendedTimeoutMillis(
+            originalTimeoutMillis = durationMillis,
+            containsIcons = true,
+            containsText = curvedText != null,
+            containsControls = false,
+        ) ?: durationMillis
+
+    LaunchedEffect(show, a11yDurationMillis) {
+        if (show) {
+            animatable.snapTo(0f)
+            animatable.animateTo(
+                targetValue = 1f,
+                animationSpec =
+                    tween(durationMillis = a11yDurationMillis.toInt(), easing = LinearEasing),
+            ) {
+                progress = value
+            }
+            onDismissRequest()
+        }
+    }
+
+    Dialog(
+        showDialog = show,
+        modifier = modifier,
+        onDismissRequest = onDismissRequest,
+        properties = properties,
+    ) {
+        Box(modifier = Modifier.fillMaxSize()) {
+            val bottomPadding =
+                if (curvedText != null)
+                    screenHeightDp() * OpenOnPhoneDialogDefaults.ExtraBottomPaddingFraction
+                else 0f
+            Box(
+                Modifier.fillMaxSize().padding(bottom = bottomPadding.dp),
+                contentAlignment = Alignment.Center
+            ) {
+                iconContainer(
+                    iconContainerColor = colors.iconContainerColor,
+                    progressIndicatorColors =
+                        ProgressIndicatorColors(
+                            SolidColor(colors.progressIndicatorColor),
+                            SolidColor(colors.progressTrackColor)
+                        ),
+                    progress = { progress }
+                )()
+                CompositionLocalProvider(LocalContentColor provides colors.iconColor) { content() }
+            }
+            CompositionLocalProvider(LocalContentColor provides colors.textColor) {
+                curvedText?.let { CurvedLayout(anchor = 90f, contentBuilder = curvedText) }
+            }
+        }
+    }
+}
+
+/** Contains the default values used by [OpenOnPhoneDialog]. */
+object OpenOnPhoneDialogDefaults {
+
+    /**
+     * A default composable used in [OpenOnPhoneDialog] that displays an open on phone icon with an
+     * animation.
+     */
+    @OptIn(ExperimentalAnimationGraphicsApi::class)
+    val Icon: @Composable BoxScope.() -> Unit = {
+        val animation =
+            AnimatedImageVector.animatedVectorResource(R.drawable.open_on_phone_animation)
+        var atEnd by remember { mutableStateOf(false) }
+        LaunchedEffect(Unit) {
+            delay(IconDelay)
+            atEnd = true
+        }
+        Icon(
+            painter = rememberAnimatedVectorPainter(animation, atEnd),
+            contentDescription = null,
+            modifier = Modifier.size(IconSize).align(Alignment.Center),
+        )
+    }
+
+    /**
+     * A default composable that displays text along a curved path, used in [OpenOnPhoneDialog].
+     *
+     * @param text The text to display. Defaults to an open on phone message.
+     * @param style The style to apply to the text. Defaults to
+     *   CurvedTextStyle(MaterialTheme.typography.titleLarge).
+     */
+    @Composable
+    fun curvedText(
+        text: String = LocalContext.current.resources.getString(R.string.wear_m3c_open_on_phone),
+        style: CurvedTextStyle = CurvedTextStyle(MaterialTheme.typography.titleLarge)
+    ): CurvedScope.() -> Unit = {
+        curvedText(
+            text = text,
+            style = style,
+            maxSweepAngle = CurvedTextDefaults.StaticContentMaxSweepAngle,
+            modifier = CurvedModifier.padding(PaddingDefaults.edgePadding),
+            angularDirection = CurvedDirection.Angular.Reversed
+        )
+    }
+
+    /**
+     * Creates a [OpenOnPhoneDialogColors] that represents the default colors used in
+     * [OpenOnPhoneDialog].
+     */
+    @Composable fun colors() = MaterialTheme.colorScheme.defaultOpenOnPhoneDialogColors
+
+    /**
+     * Creates a [OpenOnPhoneDialogColors] with modified colors used in [OpenOnPhoneDialog].
+     *
+     * @param iconColor The icon color.
+     * @param iconContainerColor The icon container color.
+     * @param progressIndicatorColor The progress indicator color.
+     * @param progressTrackColor The progress track color.
+     * @param textColor The text color.
+     */
+    @Composable
+    fun colors(
+        iconColor: Color = Color.Unspecified,
+        iconContainerColor: Color = Color.Unspecified,
+        progressIndicatorColor: Color = Color.Unspecified,
+        progressTrackColor: Color = Color.Unspecified,
+        textColor: Color = Color.Unspecified
+    ) =
+        MaterialTheme.colorScheme.defaultOpenOnPhoneDialogColors.copy(
+            iconColor = iconColor,
+            iconContainerColor = iconContainerColor,
+            progressIndicatorColor = progressIndicatorColor,
+            progressTrackColor = progressTrackColor,
+            textColor = textColor
+        )
+
+    /** Default timeout for the [OpenOnPhoneDialog] dialog, in milliseconds. */
+    const val DurationMillis = 4000L
+
+    internal val IconDelay = 67L
+    internal val SizeFraction = 0.6f
+    internal val ExtraBottomPaddingFraction = 0.02f
+    internal val IconSize = 52.dp
+
+    internal val progressIndicatorStrokeWidth = 5.dp
+    internal val progressIndicatorPadding = 5.dp
+
+    private val ColorScheme.defaultOpenOnPhoneDialogColors: OpenOnPhoneDialogColors
+        get() {
+            return mDefaultOpenOnPhoneDialogColorsCached
+                ?: OpenOnPhoneDialogColors(
+                        iconColor = fromToken(ColorSchemeKeyTokens.Primary),
+                        iconContainerColor = fromToken(ColorSchemeKeyTokens.PrimaryContainer),
+                        progressIndicatorColor = fromToken(ColorSchemeKeyTokens.Primary),
+                        progressTrackColor = fromToken(ColorSchemeKeyTokens.OnPrimary),
+                        textColor = fromToken(ColorSchemeKeyTokens.OnBackground)
+                    )
+                    .also { mDefaultOpenOnPhoneDialogColorsCached = it }
+        }
+}
+
+/**
+ * Represents the colors used in [OpenOnPhoneDialog].
+ *
+ * @param iconColor Color used to tint the icon.
+ * @param iconContainerColor The color of the container behind the icon.
+ * @param progressIndicatorColor Color used to draw the indicator arc of progress indicator.
+ * @param progressTrackColor Color used to draw the track of progress indicator.
+ * @param textColor Color used to draw the text.
+ */
+class OpenOnPhoneDialogColors(
+    val iconColor: Color,
+    val iconContainerColor: Color,
+    val progressIndicatorColor: Color,
+    val progressTrackColor: Color,
+    val textColor: Color
+) {
+    internal fun copy(
+        iconColor: Color? = null,
+        iconContainerColor: Color? = null,
+        progressIndicatorColor: Color? = null,
+        progressTrackColor: Color? = null,
+        textColor: Color? = null
+    ) =
+        OpenOnPhoneDialogColors(
+            iconColor = iconColor ?: this.iconColor,
+            iconContainerColor = iconContainerColor ?: this.iconContainerColor,
+            progressIndicatorColor = progressIndicatorColor ?: this.progressIndicatorColor,
+            progressTrackColor = progressTrackColor ?: this.progressTrackColor,
+            textColor = textColor ?: this.textColor
+        )
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || other !is OpenOnPhoneDialogColors) return false
+
+        if (iconColor != other.iconColor) return false
+        if (iconContainerColor != other.iconContainerColor) return false
+        if (progressIndicatorColor != other.progressIndicatorColor) return false
+        if (progressTrackColor != other.progressTrackColor) return false
+        if (textColor != other.textColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = iconColor.hashCode()
+        result = 31 * result + iconContainerColor.hashCode()
+        result = 31 * result + progressIndicatorColor.hashCode()
+        result = 31 * result + progressTrackColor.hashCode()
+        result = 31 * result + textColor.hashCode()
+        return result
+    }
+}
+
+private fun iconContainer(
+    iconContainerColor: Color,
+    progressIndicatorColors: ProgressIndicatorColors,
+    progress: () -> Float
+): @Composable BoxScope.() -> Unit = {
+    val size = screenWidthDp() * OpenOnPhoneDialogDefaults.SizeFraction
+    Box(Modifier.size(size.dp)) {
+        Box(
+            Modifier.fillMaxSize()
+                .padding(
+                    OpenOnPhoneDialogDefaults.progressIndicatorStrokeWidth +
+                        OpenOnPhoneDialogDefaults.progressIndicatorPadding
+                )
+                .graphicsLayer {
+                    shape = CircleShape
+                    clip = true
+                }
+                .background(iconContainerColor)
+        )
+
+        CircularProgressIndicator(
+            progress = progress,
+            strokeWidth = OpenOnPhoneDialogDefaults.progressIndicatorStrokeWidth,
+            colors = progressIndicatorColors
+        )
+    }
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
index c559aa5..20e8047 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.compose.material3
 
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.tween
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.InteractionSource
@@ -28,16 +30,19 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.takeOrElse
 import androidx.wear.compose.material3.tokens.FilledTextButtonTokens
 import androidx.wear.compose.material3.tokens.FilledTonalTextButtonTokens
+import androidx.wear.compose.material3.tokens.MotionTokens
 import androidx.wear.compose.material3.tokens.OutlinedTextButtonTokens
 import androidx.wear.compose.material3.tokens.ShapeTokens
 import androidx.wear.compose.material3.tokens.TextButtonTokens
 import androidx.wear.compose.material3.tokens.TextToggleButtonTokens
+import androidx.wear.compose.materialcore.animateSelectionColor
 
 /**
  * Wear Material [TextButton] is a circular, text-only button with transparent background and no
@@ -170,7 +175,7 @@
     onCheckedChange: (Boolean) -> Unit,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
-    colors: ToggleButtonColors = TextButtonDefaults.textToggleButtonColors(),
+    colors: TextToggleButtonColors = TextButtonDefaults.textToggleButtonColors(),
     interactionSource: MutableInteractionSource? = null,
     shape: Shape = TextButtonDefaults.shape,
     border: BorderStroke? = null,
@@ -398,7 +403,7 @@
         )
 
     /**
-     * Creates a [ToggleButtonColors] for a [TextToggleButton]
+     * Creates a [TextToggleButtonColors] for a [TextToggleButton]
      * - by default, a colored background with a contrasting content color. If the button is
      *   disabled, then the colors will have an alpha ([DisabledContainerAlpha] or
      *   [DisabledContentAlpha]) value applied.
@@ -407,7 +412,7 @@
     fun textToggleButtonColors() = MaterialTheme.colorScheme.defaultTextToggleButtonColors
 
     /**
-     * Creates a [ToggleButtonColors] for a [TextToggleButton]
+     * Creates a [TextToggleButtonColors] for a [TextToggleButton]
      * - by default, a colored background with a contrasting content color. If the button is
      *   disabled, then the colors will have an alpha ([DisabledContainerAlpha] or
      *   [DisabledContentAlpha]) value applied.
@@ -439,7 +444,7 @@
         disabledCheckedContentColor: Color = Color.Unspecified,
         disabledUncheckedContainerColor: Color = Color.Unspecified,
         disabledUncheckedContentColor: Color = Color.Unspecified,
-    ): ToggleButtonColors =
+    ): TextToggleButtonColors =
         MaterialTheme.colorScheme.defaultTextToggleButtonColors.copy(
             checkedContainerColor = checkedContainerColor,
             checkedContentColor = checkedContentColor,
@@ -575,10 +580,10 @@
                     .also { defaultTextButtonColorsCached = it }
         }
 
-    private val ColorScheme.defaultTextToggleButtonColors: ToggleButtonColors
+    private val ColorScheme.defaultTextToggleButtonColors: TextToggleButtonColors
         get() {
             return defaultTextToggleButtonColorsCached
-                ?: ToggleButtonColors(
+                ?: TextToggleButtonColors(
                         checkedContainerColor =
                             fromToken(TextToggleButtonTokens.CheckedContainerColor),
                         checkedContentColor = fromToken(TextToggleButtonTokens.CheckedContentColor),
@@ -691,3 +696,129 @@
         return result
     }
 }
+
+/**
+ * Represents the different container and content colors used for [TextToggleButton] in various
+ * states, that are checked, unchecked, enabled and disabled.
+ *
+ * @param checkedContainerColor Container or background color when the toggle button is checked
+ * @param checkedContentColor Color of the content (text or icon) when the toggle button is checked
+ * @param uncheckedContainerColor Container or background color when the toggle button is unchecked
+ * @param uncheckedContentColor Color of the content (text or icon) when the toggle button is
+ *   unchecked
+ * @param disabledCheckedContainerColor Container or background color when the toggle button is
+ *   disabled and checked
+ * @param disabledCheckedContentColor Color of content (text or icon) when the toggle button is
+ *   disabled and checked
+ * @param disabledUncheckedContainerColor Container or background color when the toggle button is
+ *   disabled and unchecked
+ * @param disabledUncheckedContentColor Color of the content (text or icon) when the toggle button
+ *   is disabled and unchecked
+ */
+@Immutable
+class TextToggleButtonColors(
+    val checkedContainerColor: Color,
+    val checkedContentColor: Color,
+    val uncheckedContainerColor: Color,
+    val uncheckedContentColor: Color,
+    val disabledCheckedContainerColor: Color,
+    val disabledCheckedContentColor: Color,
+    val disabledUncheckedContainerColor: Color,
+    val disabledUncheckedContentColor: Color,
+) {
+    internal fun copy(
+        checkedContainerColor: Color,
+        checkedContentColor: Color,
+        uncheckedContainerColor: Color,
+        uncheckedContentColor: Color,
+        disabledCheckedContainerColor: Color,
+        disabledCheckedContentColor: Color,
+        disabledUncheckedContainerColor: Color,
+        disabledUncheckedContentColor: Color,
+    ): TextToggleButtonColors =
+        TextToggleButtonColors(
+            checkedContainerColor = checkedContainerColor.takeOrElse { this.checkedContainerColor },
+            checkedContentColor = checkedContentColor.takeOrElse { this.checkedContentColor },
+            uncheckedContainerColor =
+                uncheckedContainerColor.takeOrElse { this.uncheckedContainerColor },
+            uncheckedContentColor = uncheckedContentColor.takeOrElse { this.uncheckedContentColor },
+            disabledCheckedContainerColor =
+                disabledCheckedContainerColor.takeOrElse { this.disabledCheckedContainerColor },
+            disabledCheckedContentColor =
+                disabledCheckedContentColor.takeOrElse { this.disabledCheckedContentColor },
+            disabledUncheckedContainerColor =
+                disabledUncheckedContainerColor.takeOrElse { this.disabledUncheckedContainerColor },
+            disabledUncheckedContentColor =
+                disabledUncheckedContentColor.takeOrElse { this.disabledUncheckedContentColor },
+        )
+
+    /**
+     * Determines the container color based on whether the toggle button is [enabled] and [checked].
+     *
+     * @param enabled Whether the toggle button is enabled
+     * @param checked Whether the toggle button is checked
+     */
+    @Composable
+    internal fun containerColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedContainerColor,
+            uncheckedColor = uncheckedContainerColor,
+            disabledCheckedColor = disabledCheckedContainerColor,
+            disabledUncheckedColor = disabledUncheckedContainerColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
+
+    /**
+     * Determines the content color based on whether the toggle button is [enabled] and [checked].
+     *
+     * @param enabled Whether the toggle button is enabled
+     * @param checked Whether the toggle button is checked
+     */
+    @Composable
+    internal fun contentColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedContentColor,
+            uncheckedColor = uncheckedContentColor,
+            disabledCheckedColor = disabledCheckedContentColor,
+            disabledUncheckedColor = disabledUncheckedContentColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null) return false
+        if (this::class != other::class) return false
+
+        other as TextToggleButtonColors
+
+        if (checkedContainerColor != other.checkedContainerColor) return false
+        if (checkedContentColor != other.checkedContentColor) return false
+        if (uncheckedContainerColor != other.uncheckedContainerColor) return false
+        if (uncheckedContentColor != other.uncheckedContentColor) return false
+        if (disabledCheckedContainerColor != other.disabledCheckedContainerColor) return false
+        if (disabledCheckedContentColor != other.disabledCheckedContentColor) return false
+        if (disabledUncheckedContainerColor != other.disabledUncheckedContainerColor) return false
+        if (disabledUncheckedContentColor != other.disabledUncheckedContentColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = checkedContainerColor.hashCode()
+        result = 31 * result + checkedContentColor.hashCode()
+        result = 31 * result + uncheckedContainerColor.hashCode()
+        result = 31 * result + uncheckedContentColor.hashCode()
+        result = 31 * result + disabledCheckedContainerColor.hashCode()
+        result = 31 * result + disabledCheckedContentColor.hashCode()
+        result = 31 * result + disabledUncheckedContainerColor.hashCode()
+        result = 31 * result + disabledUncheckedContentColor.hashCode()
+        return result
+    }
+}
+
+private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
+    tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButton.kt
deleted file mode 100644
index cc9cc49..0000000
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButton.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright 2024 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.wear.compose.material3
-
-import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.tween
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.State
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.takeOrElse
-import androidx.wear.compose.material3.tokens.MotionTokens
-import androidx.wear.compose.materialcore.animateSelectionColor
-
-/**
- * Represents the different container and content colors used for [IconToggleButton] and
- * [TextToggleButton]) in various states, that are checked, unchecked, enabled and disabled.
- *
- * @param checkedContainerColor Container or background color when the toggle button is checked
- * @param checkedContentColor Color of the content (text or icon) when the toggle button is checked
- * @param uncheckedContainerColor Container or background color when the toggle button is unchecked
- * @param uncheckedContentColor Color of the content (text or icon) when the toggle button is
- *   unchecked
- * @param disabledCheckedContainerColor Container or background color when the toggle button is
- *   disabled and checked
- * @param disabledCheckedContentColor Color of content (text or icon) when the toggle button is
- *   disabled and checked
- * @param disabledUncheckedContainerColor Container or background color when the toggle button is
- *   disabled and unchecked
- * @param disabledUncheckedContentColor Color of the content (text or icon) when the toggle button
- *   is disabled and unchecked
- */
-@Immutable
-class ToggleButtonColors(
-    val checkedContainerColor: Color,
-    val checkedContentColor: Color,
-    val uncheckedContainerColor: Color,
-    val uncheckedContentColor: Color,
-    val disabledCheckedContainerColor: Color,
-    val disabledCheckedContentColor: Color,
-    val disabledUncheckedContainerColor: Color,
-    val disabledUncheckedContentColor: Color,
-) {
-    internal fun copy(
-        checkedContainerColor: Color,
-        checkedContentColor: Color,
-        uncheckedContainerColor: Color,
-        uncheckedContentColor: Color,
-        disabledCheckedContainerColor: Color,
-        disabledCheckedContentColor: Color,
-        disabledUncheckedContainerColor: Color,
-        disabledUncheckedContentColor: Color,
-    ): ToggleButtonColors =
-        ToggleButtonColors(
-            checkedContainerColor = checkedContainerColor.takeOrElse { this.checkedContainerColor },
-            checkedContentColor = checkedContentColor.takeOrElse { this.checkedContentColor },
-            uncheckedContainerColor =
-                uncheckedContainerColor.takeOrElse { this.uncheckedContainerColor },
-            uncheckedContentColor = uncheckedContentColor.takeOrElse { this.uncheckedContentColor },
-            disabledCheckedContainerColor =
-                disabledCheckedContainerColor.takeOrElse { this.disabledCheckedContainerColor },
-            disabledCheckedContentColor =
-                disabledCheckedContentColor.takeOrElse { this.disabledCheckedContentColor },
-            disabledUncheckedContainerColor =
-                disabledUncheckedContainerColor.takeOrElse { this.disabledUncheckedContainerColor },
-            disabledUncheckedContentColor =
-                disabledUncheckedContentColor.takeOrElse { this.disabledUncheckedContentColor },
-        )
-
-    /**
-     * Determines the container color based on whether the toggle button is [enabled] and [checked].
-     *
-     * @param enabled Whether the toggle button is enabled
-     * @param checked Whether the toggle button is checked
-     */
-    @Composable
-    internal fun containerColor(enabled: Boolean, checked: Boolean): State<Color> =
-        animateSelectionColor(
-            enabled = enabled,
-            checked = checked,
-            checkedColor = checkedContainerColor,
-            uncheckedColor = uncheckedContainerColor,
-            disabledCheckedColor = disabledCheckedContainerColor,
-            disabledUncheckedColor = disabledUncheckedContainerColor,
-            animationSpec = COLOR_ANIMATION_SPEC
-        )
-
-    /**
-     * Determines the content color based on whether the toggle button is [enabled] and [checked].
-     *
-     * @param enabled Whether the toggle button is enabled
-     * @param checked Whether the toggle button is checked
-     */
-    @Composable
-    internal fun contentColor(enabled: Boolean, checked: Boolean): State<Color> =
-        animateSelectionColor(
-            enabled = enabled,
-            checked = checked,
-            checkedColor = checkedContentColor,
-            uncheckedColor = uncheckedContentColor,
-            disabledCheckedColor = disabledCheckedContentColor,
-            disabledUncheckedColor = disabledUncheckedContentColor,
-            animationSpec = COLOR_ANIMATION_SPEC
-        )
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other == null) return false
-        if (this::class != other::class) return false
-
-        other as ToggleButtonColors
-
-        if (checkedContainerColor != other.checkedContainerColor) return false
-        if (checkedContentColor != other.checkedContentColor) return false
-        if (uncheckedContainerColor != other.uncheckedContainerColor) return false
-        if (uncheckedContentColor != other.uncheckedContentColor) return false
-        if (disabledCheckedContainerColor != other.disabledCheckedContainerColor) return false
-        if (disabledCheckedContentColor != other.disabledCheckedContentColor) return false
-        if (disabledUncheckedContainerColor != other.disabledUncheckedContainerColor) return false
-        if (disabledUncheckedContentColor != other.disabledUncheckedContentColor) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = checkedContainerColor.hashCode()
-        result = 31 * result + checkedContentColor.hashCode()
-        result = 31 * result + uncheckedContainerColor.hashCode()
-        result = 31 * result + uncheckedContentColor.hashCode()
-        result = 31 * result + disabledCheckedContainerColor.hashCode()
-        result = 31 * result + disabledCheckedContentColor.hashCode()
-        result = 31 * result + disabledUncheckedContainerColor.hashCode()
-        result = 31 * result + disabledUncheckedContentColor.hashCode()
-        return result
-    }
-}
-
-private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
-    tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
diff --git a/wear/compose/compose-material3/src/main/res/drawable/check_animation.xml b/wear/compose/compose-material3/src/main/res/drawable/check_animation.xml
new file mode 100644
index 0000000..90fd02a
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/res/drawable/check_animation.xml
@@ -0,0 +1,17 @@
+<!--
+  Copyright 2024 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.
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="300dp" android:width="400dp" android:viewportHeight="300" android:viewportWidth="400"><group android:name="_R_G"><group android:name="_R_G_L_0_G" android:translateX="200" android:translateY="176" android:scaleX="1.05507" android:scaleY="1.05556"><path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="34" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M-147.5 -20 C-147.5,-20 -51.5,76 -51.5,76 C-51.5,76 151.5,-127 151.5,-127 "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="250" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="10017" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/main/res/drawable/failure_animation.xml b/wear/compose/compose-material3/src/main/res/drawable/failure_animation.xml
new file mode 100644
index 0000000..2c5120b
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/res/drawable/failure_animation.xml
@@ -0,0 +1,17 @@
+<!--
+  Copyright 2024 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.
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="800dp" android:width="800dp" android:viewportHeight="800" android:viewportWidth="800"><group android:name="_R_G"><group android:name="_R_G_L_2_G" android:translateX="414" android:translateY="398" android:scaleX="10.57" android:scaleY="10.57"><path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#ff8986" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-14.49 17.77 C-14.49,17.77 -14.49,19.61 -14.49,19.61 C-14.49,19.72 -14.45,19.81 -14.38,19.88 C-14.31,19.95 -14.22,19.99 -14.11,19.99 C-14.11,19.99 7.78,19.99 7.78,19.99 C7.88,19.99 7.97,19.95 8.05,19.88 C8.12,19.81 8.15,19.72 8.15,19.61 C8.15,19.61 8.15,17.77 8.15,17.77 C8.15,17.77 -14.49,17.77 -14.49,17.77c  M-14.49 -17.77 C-14.49,-17.77 8.15,-17.77 8.15,-17.77 C8.15,-17.77 8.15,-19.61 8.15,-19.61 C8.15,-19.72 8.12,-19.81 8.05,-19.88 C7.97,-19.95 7.88,-19.99 7.78,-19.99 C7.78,-19.99 -14.11,-19.99 -14.11,-19.99 C-14.22,-19.99 -14.31,-19.95 -14.38,-19.88 C-14.45,-19.81 -14.49,-19.72 -14.49,-19.61 C-14.49,-19.61 -14.49,-17.77 -14.49,-17.77c  M-14.11 23.29 C-15.12,23.29 -15.99,22.93 -16.71,22.21 C-17.43,21.49 -17.79,20.62 -17.79,19.61 C-17.79,19.61 -17.79,-19.61 -17.79,-19.61 C-17.79,-20.62 -17.43,-21.49 -16.71,-22.21 C-15.99,-22.93 -15.12,-23.29 -14.11,-23.29 C-14.11,-23.29 7.78,-23.29 7.78,-23.29 C8.79,-23.29 9.65,-22.93 10.38,-22.21 C11.1,-21.49 11.46,-20.62 11.46,-19.61 C11.46,-19.61 11.46,-13.32 11.46,-13.32 C11.46,-12.86 11.3,-12.46 10.97,-12.13 C10.65,-11.81 10.25,-11.65 9.78,-11.65 C9.31,-11.65 8.91,-11.81 8.59,-12.13 C8.3,-12.46 8.15,-12.86 8.15,-13.32 C8.15,-13.32 8.15,-14.46 8.15,-14.46 C8.15,-14.46 -14.49,-14.46 -14.49,-14.46 C-14.49,-14.46 -14.49,14.46 -14.49,14.46 C-14.49,14.46 8.15,14.46 8.15,14.46 C8.15,14.46 8.15,13.33 8.15,13.33 C8.15,12.86 8.3,12.46 8.59,12.13 C8.91,11.81 9.31,11.65 9.78,11.65 C10.25,11.65 10.65,11.81 10.97,12.13 C11.3,12.46 11.46,12.86 11.46,13.33 C11.46,13.33 11.46,19.61 11.46,19.61 C11.46,20.62 11.1,21.49 10.38,22.21 C9.65,22.93 8.79,23.29 7.78,23.29 C7.78,23.29 -14.11,23.29 -14.11,23.29c "/></group><group android:name="_R_G_L_1_G" android:translateX="400" android:translateY="400"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#003352" android:fillAlpha="1" android:fillType="nonZero" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M52 61 C52,61 179,-67 179,-67 "/><path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#ff8986" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="34" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M52 61 C52,61 179,-67 179,-67 "/></group><group android:name="_R_G_L_0_G" android:translateX="400" android:translateY="400"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#003352" android:fillAlpha="1" android:fillType="nonZero" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M52 -66 C52,-66 177,60 177,60 "/><path android:name="_R_G_L_0_G_D_1_P_0" android:strokeColor="#ff8986" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="35" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M52 -66 C52,-66 177,60 177,60 "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="100" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="267" android:startOffset="100" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="100" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="267" android:startOffset="100" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="267" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="267" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="10017" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/main/res/drawable/open_on_phone_animation.xml b/wear/compose/compose-material3/src/main/res/drawable/open_on_phone_animation.xml
new file mode 100644
index 0000000..26c7f42
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/res/drawable/open_on_phone_animation.xml
@@ -0,0 +1,17 @@
+<!--
+  Copyright 2024 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.
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="800dp" android:width="800dp" android:viewportHeight="800" android:viewportWidth="800"><group android:name="_R_G"><group android:name="_R_G_L_2_G" android:translateX="372" android:translateY="396" android:scaleX="9.86" android:scaleY="9.86"><path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#d3e3fd" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-7.44 23.29 C-8.45,23.29 -9.32,22.93 -10.04,22.21 C-10.76,21.49 -11.13,20.62 -11.13,19.61 C-11.13,19.61 -11.13,8.18 -11.13,8.18 C-11.13,7.71 -10.96,7.31 -10.64,6.99 C-10.31,6.66 -9.91,6.5 -9.45,6.5 C-8.98,6.5 -8.6,6.66 -8.31,6.99 C-7.98,7.31 -7.82,7.71 -7.82,8.18 C-7.82,8.18 -7.82,14.46 -7.82,14.46 C-7.82,14.46 14.82,14.46 14.82,14.46 C14.82,14.46 14.82,-14.46 14.82,-14.46 C14.82,-14.46 -7.82,-14.46 -7.82,-14.46 C-7.82,-14.46 -7.82,-8.18 -7.82,-8.18 C-7.82,-7.71 -7.98,-7.31 -8.31,-6.99 C-8.6,-6.66 -8.98,-6.5 -9.45,-6.5 C-9.91,-6.5 -10.31,-6.66 -10.64,-6.99 C-10.96,-7.31 -11.13,-7.71 -11.13,-8.18 C-11.13,-8.18 -11.13,-19.61 -11.13,-19.61 C-11.13,-20.62 -10.76,-21.49 -10.04,-22.21 C-9.32,-22.93 -8.45,-23.29 -7.44,-23.29 C-7.44,-23.29 14.44,-23.29 14.44,-23.29 C15.45,-23.29 16.32,-22.93 17.04,-22.21 C17.76,-21.49 18.12,-20.62 18.12,-19.61 C18.12,-19.61 18.12,19.61 18.12,19.61 C18.12,20.62 17.76,21.49 17.04,22.21 C16.32,22.93 15.45,23.29 14.44,23.29 C14.44,23.29 -7.44,23.29 -7.44,23.29c  M-7.82 17.77 C-7.82,17.77 -7.82,19.61 -7.82,19.61 C-7.82,19.72 -7.78,19.81 -7.71,19.88 C-7.64,19.95 -7.55,19.99 -7.44,19.99 C-7.44,19.99 14.44,19.99 14.44,19.99 C14.55,19.99 14.64,19.95 14.71,19.88 C14.78,19.81 14.82,19.72 14.82,19.61 C14.82,19.61 14.82,17.77 14.82,17.77 C14.82,17.77 -7.82,17.77 -7.82,17.77c  M-7.82 -17.77 C-7.82,-17.77 14.82,-17.77 14.82,-17.77 C14.82,-17.77 14.82,-19.61 14.82,-19.61 C14.82,-19.72 14.78,-19.81 14.71,-19.88 C14.64,-19.95 14.55,-19.99 14.44,-19.99 C14.44,-19.99 -7.44,-19.99 -7.44,-19.99 C-7.55,-19.99 -7.64,-19.95 -7.71,-19.88 C-7.78,-19.81 -7.82,-19.72 -7.82,-19.61 C-7.82,-19.61 -7.82,-17.77 -7.82,-17.77c "/></group><group android:name="_R_G_L_1_G" android:translateX="370" android:translateY="428" android:scaleY="0"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#003352" android:fillAlpha="1" android:fillType="nonZero" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M-189 -33 C-189,-33 20,-33 20,-33 "/><path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="30" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M-189 -33 C-189,-33 20,-33 20,-33 "/></group><group android:name="_R_G_L_0_G_T_1" android:translateX="180" android:translateY="395" android:scaleY="0"><group android:name="_R_G_L_0_G" android:translateX="-20" android:translateY="33"><path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="32" android:strokeAlpha="1" android:trimPathStart="0.49" android:trimPathEnd="0.51" android:trimPathOffset="0" android:pathData=" M-36 -90 C-36,-90 21,-33 21,-33 C21,-33 -36,24 -36,24 "/></group></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="217" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="217" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateXY" android:duration="67" android:startOffset="0" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 370,428C 370,428 370,428 370,428"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="217" android:startOffset="67" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 370,428C 370,428 412,428 412,428"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="200" android:startOffset="283" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 412,428C 412,428 400,428 400,428"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.44,0 0.55,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathStart" android:duration="133" android:startOffset="0" android:valueFrom="0.49" android:valueTo="0.49" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathStart" android:duration="100" android:startOffset="133" android:valueFrom="0.49" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="133" android:startOffset="0" android:valueFrom="0.51" android:valueTo="0.51" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="100" android:startOffset="133" android:valueFrom="0.51" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_T_1"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateXY" android:duration="67" android:startOffset="0" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 180,395C 180,395 180,395 180,395"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="217" android:startOffset="67" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 180,395C 180,395 432,395 432,395"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="200" android:startOffset="283" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 432,395C 432,395 420,395 420,395"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.44,0 0.55,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_T_1"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="10017" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/main/res/values-af/strings.xml b/wear/compose/compose-material3/src/main/res/values-af/strings.xml
index d19c450..4951307 100644
--- a/wear/compose/compose-material3/src/main/res/values-af/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-af/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d sekonde</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Tydperk"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bevestig"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-am/strings.xml b/wear/compose/compose-material3/src/main/res/values-am/strings.xml
index ee40287..da5ab9c 100644
--- a/wear/compose/compose-material3/src/main/res/values-am/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-am/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">%d ሰከንዶች</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ክፍለ ጊዜ"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"አረጋግጥ"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ar/strings.xml b/wear/compose/compose-material3/src/main/res/values-ar/strings.xml
index 6f65d6e..450227f 100644
--- a/wear/compose/compose-material3/src/main/res/values-ar/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ar/strings.xml
@@ -45,5 +45,13 @@
       <item quantity="one">ثانية واحدة</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"فترة"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"تأكيد"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-as/strings.xml b/wear/compose/compose-material3/src/main/res/values-as/strings.xml
index 867bba0..178eaa4 100644
--- a/wear/compose/compose-material3/src/main/res/values-as/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-as/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">%d ছেকেণ্ড</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"পিৰিয়ড"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"নিশ্চিত কৰক"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-az/strings.xml b/wear/compose/compose-material3/src/main/res/values-az/strings.xml
index 43fc687..de43d4f 100644
--- a/wear/compose/compose-material3/src/main/res/values-az/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-az/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d saniyə</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Müddət"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Təsdiq edin"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml b/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml
index 7130d77..eca919a 100644
--- a/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="other">%d sekundi</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potvrdi"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-be/strings.xml b/wear/compose/compose-material3/src/main/res/values-be/strings.xml
index 1425da3..dc5e832 100644
--- a/wear/compose/compose-material3/src/main/res/values-be/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-be/strings.xml
@@ -39,5 +39,13 @@
       <item quantity="other">%d секунды</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Перыяд"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Пацвердзіць"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-bg/strings.xml b/wear/compose/compose-material3/src/main/res/values-bg/strings.xml
index a2b7c61..2866f08 100644
--- a/wear/compose/compose-material3/src/main/res/values-bg/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-bg/strings.xml
@@ -33,5 +33,9 @@
       <item quantity="one">%d секунда</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Точка"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Ден"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Месец"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Година"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Потвърждаване"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Напред"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-bn/strings.xml b/wear/compose/compose-material3/src/main/res/values-bn/strings.xml
index b68d6d9..34a09f1 100644
--- a/wear/compose/compose-material3/src/main/res/values-bn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-bn/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">%d সেকেন্ড</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"সময়সীমা"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"কনফার্ম করুন"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-bs/strings.xml b/wear/compose/compose-material3/src/main/res/values-bs/strings.xml
index 0654f2f..9e53fc8 100644
--- a/wear/compose/compose-material3/src/main/res/values-bs/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-bs/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="other">%d sekundi</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potvrđivanje"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ca/strings.xml b/wear/compose/compose-material3/src/main/res/values-ca/strings.xml
index cd62e3f..0c19945 100644
--- a/wear/compose/compose-material3/src/main/res/values-ca/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ca/strings.xml
@@ -36,5 +36,9 @@
       <item quantity="one">%d segon</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Període"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Dia"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Mes"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Any"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirma"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Següent"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-cs/strings.xml b/wear/compose/compose-material3/src/main/res/values-cs/strings.xml
index e8dfcdc..98dc3ea 100644
--- a/wear/compose/compose-material3/src/main/res/values-cs/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-cs/strings.xml
@@ -39,5 +39,13 @@
       <item quantity="one">%d sekunda</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Období"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potvrdit"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-da/strings.xml b/wear/compose/compose-material3/src/main/res/values-da/strings.xml
index 340890b..d1bd436 100644
--- a/wear/compose/compose-material3/src/main/res/values-da/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-da/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">%d sekunder</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Format"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bekræft"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-de/strings.xml b/wear/compose/compose-material3/src/main/res/values-de/strings.xml
index fd7b287..9fdd438 100644
--- a/wear/compose/compose-material3/src/main/res/values-de/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-de/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d Sekunde</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Zeitraum"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bestätigen"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-el/strings.xml b/wear/compose/compose-material3/src/main/res/values-el/strings.xml
index b105e52..20374c9 100644
--- a/wear/compose/compose-material3/src/main/res/values-el/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-el/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d δευτερόλεπτο</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Περίοδος"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Επιβεβαίωση"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml
index b52363a..58982b2 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d second</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirm"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml
index 37d75e2..fb5c42c 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml
@@ -33,5 +33,9 @@
       <item quantity="one">%d Second</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Day"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Month"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Year"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirm"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Next"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml
index b52363a..58982b2 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d second</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirm"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml
index b52363a..58982b2 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d second</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirm"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rXC/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rXC/strings.xml
index ba2dc8b..0bff2a4 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rXC/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rXC/strings.xml
@@ -33,5 +33,9 @@
       <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‎‎‎‎‎‎‎‏‎‏‎‎%d Second‎‏‎‎‏‎</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‎‎‎Period‎‏‎‎‏‎"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎‏‏‎‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‎Day‎‏‎‎‏‎"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎Month‎‏‎‎‏‎"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎Year‎‏‎‎‏‎"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎Confirm‎‏‎‎‏‎"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎Next‎‏‎‎‏‎"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-es-rUS/strings.xml b/wear/compose/compose-material3/src/main/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..5dac2b5
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/res/values-es-rUS/strings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wear_m3c_time_picker_hour" msgid="5670838450714030035">"Hora"</string>
+    <string name="wear_m3c_time_picker_minute" msgid="2847700380677127030">"Minuto"</string>
+    <string name="wear_m3c_time_picker_second" msgid="5551916170669814925">"Segundo"</string>
+    <plurals name="wear_m3c_time_picker_hours_content_description" formatted="false" msgid="7688673698789346225">
+      <item quantity="many">%d de horas</item>
+      <item quantity="other">%d horas</item>
+      <item quantity="one">%d hora</item>
+    </plurals>
+    <plurals name="wear_m3c_time_picker_minutes_content_description" formatted="false" msgid="8268405448590438607">
+      <item quantity="many">%d de minutos</item>
+      <item quantity="other">%d minutos</item>
+      <item quantity="one">%d minuto</item>
+    </plurals>
+    <plurals name="wear_m3c_time_picker_seconds_content_description" formatted="false" msgid="1073969431850983434">
+      <item quantity="many">%d de segundos</item>
+      <item quantity="other">%d segundos</item>
+      <item quantity="one">%d segundo</item>
+    </plurals>
+    <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Período"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
+    <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
+</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-es/strings.xml b/wear/compose/compose-material3/src/main/res/values-es/strings.xml
new file mode 100644
index 0000000..7ed6eed
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/res/values-es/strings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wear_m3c_time_picker_hour" msgid="5670838450714030035">"Hora"</string>
+    <string name="wear_m3c_time_picker_minute" msgid="2847700380677127030">"Minuto"</string>
+    <string name="wear_m3c_time_picker_second" msgid="5551916170669814925">"Segundo"</string>
+    <plurals name="wear_m3c_time_picker_hours_content_description" formatted="false" msgid="7688673698789346225">
+      <item quantity="many">%d horas</item>
+      <item quantity="other">%d horas</item>
+      <item quantity="one">%d hora</item>
+    </plurals>
+    <plurals name="wear_m3c_time_picker_minutes_content_description" formatted="false" msgid="8268405448590438607">
+      <item quantity="many">%d minutos</item>
+      <item quantity="other">%d minutos</item>
+      <item quantity="one">%d minuto</item>
+    </plurals>
+    <plurals name="wear_m3c_time_picker_seconds_content_description" formatted="false" msgid="1073969431850983434">
+      <item quantity="many">%d segundos</item>
+      <item quantity="other">%d segundos</item>
+      <item quantity="one">%d segundo</item>
+    </plurals>
+    <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periodo"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
+    <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
+</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-et/strings.xml b/wear/compose/compose-material3/src/main/res/values-et/strings.xml
index e2ae406..898d4f1 100644
--- a/wear/compose/compose-material3/src/main/res/values-et/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-et/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d sekund</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periood"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Kinnita"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-eu/strings.xml b/wear/compose/compose-material3/src/main/res/values-eu/strings.xml
index 98f7e97..202e7ee 100644
--- a/wear/compose/compose-material3/src/main/res/values-eu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-eu/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d segundo</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Epea"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Berretsi"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-fa/strings.xml b/wear/compose/compose-material3/src/main/res/values-fa/strings.xml
index e51ff20..8f9c137 100644
--- a/wear/compose/compose-material3/src/main/res/values-fa/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fa/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">‏‫%d ثانیه</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"مدت زمان"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"تأیید کردن"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-fi/strings.xml b/wear/compose/compose-material3/src/main/res/values-fi/strings.xml
index 7a0c590..0f0b50b 100644
--- a/wear/compose/compose-material3/src/main/res/values-fi/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fi/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d sekunti</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Jakso"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Vahvista"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml b/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml
index e1e9794..cdc3ad5 100644
--- a/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="other">%d secondes</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Période"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmer"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-fr/strings.xml b/wear/compose/compose-material3/src/main/res/values-fr/strings.xml
index 97370e0..bdb1f1b 100644
--- a/wear/compose/compose-material3/src/main/res/values-fr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fr/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="other">%d secondes</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Période"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmer"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-gl/strings.xml b/wear/compose/compose-material3/src/main/res/values-gl/strings.xml
index 1f5b0d0..ad5ca33 100644
--- a/wear/compose/compose-material3/src/main/res/values-gl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-gl/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d segundo</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Período"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-gu/strings.xml b/wear/compose/compose-material3/src/main/res/values-gu/strings.xml
index cd43c67..b01e9be 100644
--- a/wear/compose/compose-material3/src/main/res/values-gu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-gu/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">%d સેકન્ડ</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"અવધિ"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"કન્ફર્મ કરો"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-hi/strings.xml b/wear/compose/compose-material3/src/main/res/values-hi/strings.xml
index 0d50a66..c861b3af 100644
--- a/wear/compose/compose-material3/src/main/res/values-hi/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hi/strings.xml
@@ -33,5 +33,9 @@
       <item quantity="other">%d सेकंड</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"समयअवधि"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"दिन"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"महीना"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"साल"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"पुष्टि करें"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"अगला"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-hr/strings.xml b/wear/compose/compose-material3/src/main/res/values-hr/strings.xml
index 71b0d59..af212a5 100644
--- a/wear/compose/compose-material3/src/main/res/values-hr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hr/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="other">%d sekundi</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Razdoblje"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potvrdi"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-hu/strings.xml b/wear/compose/compose-material3/src/main/res/values-hu/strings.xml
index ead4509..2cce16e 100644
--- a/wear/compose/compose-material3/src/main/res/values-hu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hu/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d másodperc</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Időszak"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Megerősítés"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-hy/strings.xml b/wear/compose/compose-material3/src/main/res/values-hy/strings.xml
index bfff0c2..cda99db 100644
--- a/wear/compose/compose-material3/src/main/res/values-hy/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hy/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">%d վայրկյան</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Ժամանակահատված"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Հաստատել"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-in/strings.xml b/wear/compose/compose-material3/src/main/res/values-in/strings.xml
index aba3a73..66a973e 100644
--- a/wear/compose/compose-material3/src/main/res/values-in/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-in/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d Detik</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Jangka waktu"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Konfirmasi"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-is/strings.xml b/wear/compose/compose-material3/src/main/res/values-is/strings.xml
index 1065161..8983c3f 100644
--- a/wear/compose/compose-material3/src/main/res/values-is/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-is/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">%d sekúndur</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Punktur"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Staðfesta"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-it/strings.xml b/wear/compose/compose-material3/src/main/res/values-it/strings.xml
index 33d9d41..a9566b7 100644
--- a/wear/compose/compose-material3/src/main/res/values-it/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-it/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="one">%d secondo</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periodo"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Conferma"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-iw/strings.xml b/wear/compose/compose-material3/src/main/res/values-iw/strings.xml
index 320aeef..91f1e27 100644
--- a/wear/compose/compose-material3/src/main/res/values-iw/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-iw/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="other">‏‫%d שניות</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"תקופת זמן"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"אישור"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ja/strings.xml b/wear/compose/compose-material3/src/main/res/values-ja/strings.xml
index 2b82492..65e9cc7 100644
--- a/wear/compose/compose-material3/src/main/res/values-ja/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ja/strings.xml
@@ -33,5 +33,9 @@
       <item quantity="one">%d 秒</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"期間"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"日"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"月"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"年"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"確認"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"次へ"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ka/strings.xml b/wear/compose/compose-material3/src/main/res/values-ka/strings.xml
index f1930fd..4f4734e 100644
--- a/wear/compose/compose-material3/src/main/res/values-ka/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ka/strings.xml
@@ -33,5 +33,9 @@
       <item quantity="one">%d წამი</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"პერიოდი"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"დღე"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"თვე"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"წელი"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"დადასტურება"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"შემდეგი"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-kk/strings.xml b/wear/compose/compose-material3/src/main/res/values-kk/strings.xml
index 0b79463..e0d8b3d 100644
--- a/wear/compose/compose-material3/src/main/res/values-kk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-kk/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d секунд</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Кезең"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Растау"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-km/strings.xml b/wear/compose/compose-material3/src/main/res/values-km/strings.xml
index b302b3b..410a6f2 100644
--- a/wear/compose/compose-material3/src/main/res/values-km/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-km/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d វិនាទី</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"រយៈពេល"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"បញ្ជាក់"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-kn/strings.xml b/wear/compose/compose-material3/src/main/res/values-kn/strings.xml
index fe7c9cf..6ae6994 100644
--- a/wear/compose/compose-material3/src/main/res/values-kn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-kn/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">%d ಸೆಕೆಂಡ್‌ಗಳು</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ಅವಧಿ"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"ದೃಢೀಕರಿಸಿ"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ko/strings.xml b/wear/compose/compose-material3/src/main/res/values-ko/strings.xml
index 231ff8f5..45f8446 100644
--- a/wear/compose/compose-material3/src/main/res/values-ko/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ko/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d초</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"기간"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"확인"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ky/strings.xml b/wear/compose/compose-material3/src/main/res/values-ky/strings.xml
index efbade7..692726e 100644
--- a/wear/compose/compose-material3/src/main/res/values-ky/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ky/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d секунд</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Чекит"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Ырастоо"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-lo/strings.xml b/wear/compose/compose-material3/src/main/res/values-lo/strings.xml
index 8e4a64e..59d2d94 100644
--- a/wear/compose/compose-material3/src/main/res/values-lo/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-lo/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d ວິນາທີ</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ໄລຍະເວລາ"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"ຢືນຢັນ"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-lt/strings.xml b/wear/compose/compose-material3/src/main/res/values-lt/strings.xml
index ad7f022..615054e 100644
--- a/wear/compose/compose-material3/src/main/res/values-lt/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-lt/strings.xml
@@ -39,5 +39,13 @@
       <item quantity="other">%d sekundžių</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Laikotarpis"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Patvirtinti"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-lv/strings.xml b/wear/compose/compose-material3/src/main/res/values-lv/strings.xml
index 1057111..08f2d2c 100644
--- a/wear/compose/compose-material3/src/main/res/values-lv/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-lv/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="other">%d sekundes</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periods"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Apstiprināt"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-mk/strings.xml b/wear/compose/compose-material3/src/main/res/values-mk/strings.xml
index 3e57b97..a093425 100644
--- a/wear/compose/compose-material3/src/main/res/values-mk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-mk/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">%d секунди</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Период"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Потврди"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ml/strings.xml b/wear/compose/compose-material3/src/main/res/values-ml/strings.xml
index ff5642f..36d8b54 100644
--- a/wear/compose/compose-material3/src/main/res/values-ml/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ml/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d സെക്കൻഡ്</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"കാലയളവ്"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"സ്ഥിരീകരിക്കുക"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-mn/strings.xml b/wear/compose/compose-material3/src/main/res/values-mn/strings.xml
index 7db8e1c..629e16a 100644
--- a/wear/compose/compose-material3/src/main/res/values-mn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-mn/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d секунд</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Хугацаа"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Баталгаажуулах"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-mr/strings.xml b/wear/compose/compose-material3/src/main/res/values-mr/strings.xml
index 23e9efe..904ad4f 100644
--- a/wear/compose/compose-material3/src/main/res/values-mr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-mr/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d सेकंद</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"कालावधी"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"कन्फर्म करा"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ms/strings.xml b/wear/compose/compose-material3/src/main/res/values-ms/strings.xml
index 5869290..df4b8c9 100644
--- a/wear/compose/compose-material3/src/main/res/values-ms/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ms/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d Saat</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Tempoh"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Sahkan"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-my/strings.xml b/wear/compose/compose-material3/src/main/res/values-my/strings.xml
index f328281..aa85494 100644
--- a/wear/compose/compose-material3/src/main/res/values-my/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-my/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d စက္ကန့်</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"အချိန်ကာလ"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"အတည်ပြုရန်"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-nb/strings.xml b/wear/compose/compose-material3/src/main/res/values-nb/strings.xml
index b4fd2e7..0d9dbd3 100644
--- a/wear/compose/compose-material3/src/main/res/values-nb/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-nb/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d sekund</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periode"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bekreft"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ne/strings.xml b/wear/compose/compose-material3/src/main/res/values-ne/strings.xml
index b234e16..4f5d479 100644
--- a/wear/compose/compose-material3/src/main/res/values-ne/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ne/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d सेकेन्ड</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"अवधि"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"पुष्टि गर्नुहोस्"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-nl/strings.xml b/wear/compose/compose-material3/src/main/res/values-nl/strings.xml
index 3afe2c9..8561545 100644
--- a/wear/compose/compose-material3/src/main/res/values-nl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-nl/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d seconde</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periode"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bevestigen"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-or/strings.xml b/wear/compose/compose-material3/src/main/res/values-or/strings.xml
index acf7af8..7701b598 100644
--- a/wear/compose/compose-material3/src/main/res/values-or/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-or/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d ସେକେଣ୍ଡ</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ଅବଧି"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-pa/strings.xml b/wear/compose/compose-material3/src/main/res/values-pa/strings.xml
index 80dbbc1..1000cde 100644
--- a/wear/compose/compose-material3/src/main/res/values-pa/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pa/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">%d ਸਕਿੰਟ</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ਮਿਆਦ"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"ਤਸਦੀਕ ਕਰੋ"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-pl/strings.xml b/wear/compose/compose-material3/src/main/res/values-pl/strings.xml
index 84a021b..5d56d82 100644
--- a/wear/compose/compose-material3/src/main/res/values-pl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pl/strings.xml
@@ -39,5 +39,13 @@
       <item quantity="one">%d sekunda</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Kropka"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potwierdź"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml b/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml
index 2dcdcbf..77c3710 100644
--- a/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="other">%d segundos</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Período"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml b/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml
index 9bbdb89..416cbc0 100644
--- a/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml
@@ -36,5 +36,9 @@
       <item quantity="one">%d segundo</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Período"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Dia"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Mês"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Ano"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Seguinte"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-pt/strings.xml b/wear/compose/compose-material3/src/main/res/values-pt/strings.xml
index 2dcdcbf..77c3710 100644
--- a/wear/compose/compose-material3/src/main/res/values-pt/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pt/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="other">%d segundos</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Período"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ro/strings.xml b/wear/compose/compose-material3/src/main/res/values-ro/strings.xml
index a682983b..f9723df 100644
--- a/wear/compose/compose-material3/src/main/res/values-ro/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ro/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="one">%d secundă</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Perioada"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmă"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ru/strings.xml b/wear/compose/compose-material3/src/main/res/values-ru/strings.xml
index 47fb6f8..e01e3c3 100644
--- a/wear/compose/compose-material3/src/main/res/values-ru/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ru/strings.xml
@@ -39,5 +39,13 @@
       <item quantity="other">%d секунды</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Период"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Подтвердить"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-si/strings.xml b/wear/compose/compose-material3/src/main/res/values-si/strings.xml
index 491d592..6f3d4c6 100644
--- a/wear/compose/compose-material3/src/main/res/values-si/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-si/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">තත්පර %d</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"කාල පරිච්ඡේදය"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"තහවුරු කරන්න"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sk/strings.xml b/wear/compose/compose-material3/src/main/res/values-sk/strings.xml
index 7b45b82..8840530 100644
--- a/wear/compose/compose-material3/src/main/res/values-sk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sk/strings.xml
@@ -39,5 +39,13 @@
       <item quantity="one">%d sekunda</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Obdobie"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potvrdiť"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sl/strings.xml b/wear/compose/compose-material3/src/main/res/values-sl/strings.xml
index 164b53e..cc79d76 100644
--- a/wear/compose/compose-material3/src/main/res/values-sl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sl/strings.xml
@@ -39,5 +39,9 @@
       <item quantity="other">%d sekund</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Pika"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Dan"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Mesec"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Leto"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potrdi"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Naprej"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sq/strings.xml b/wear/compose/compose-material3/src/main/res/values-sq/strings.xml
index 149c7bb..411d53e 100644
--- a/wear/compose/compose-material3/src/main/res/values-sq/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sq/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d sekondë</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periudha"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Konfirmo"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sr/strings.xml b/wear/compose/compose-material3/src/main/res/values-sr/strings.xml
index decaa24c..661b6d2 100644
--- a/wear/compose/compose-material3/src/main/res/values-sr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sr/strings.xml
@@ -36,5 +36,13 @@
       <item quantity="other">%d секунди</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Период"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Потврди"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sv/strings.xml b/wear/compose/compose-material3/src/main/res/values-sv/strings.xml
index 33c96f7..b395ba1d 100644
--- a/wear/compose/compose-material3/src/main/res/values-sv/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sv/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d sekund</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Punkt"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bekräfta"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sw/strings.xml b/wear/compose/compose-material3/src/main/res/values-sw/strings.xml
index d1cc257..aad1959 100644
--- a/wear/compose/compose-material3/src/main/res/values-sw/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sw/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">Sekunde %d</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Kipindi"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Thibitisha"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ta/strings.xml b/wear/compose/compose-material3/src/main/res/values-ta/strings.xml
index 669314b..aacd3cd 100644
--- a/wear/compose/compose-material3/src/main/res/values-ta/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ta/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d வினாடி</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"கால இடைவெளி"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"உறுதிசெய்யும்"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-te/strings.xml b/wear/compose/compose-material3/src/main/res/values-te/strings.xml
index 353d9fc..2374063 100644
--- a/wear/compose/compose-material3/src/main/res/values-te/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-te/strings.xml
@@ -33,5 +33,9 @@
       <item quantity="one">%d సెకను</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"వ్యవధి"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"రోజు"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"నెల"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"సంవత్సరం"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"నిర్ధారించండి"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"తర్వాత"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-th/strings.xml b/wear/compose/compose-material3/src/main/res/values-th/strings.xml
index 3bd9b5c..d1ad793 100644
--- a/wear/compose/compose-material3/src/main/res/values-th/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-th/strings.xml
@@ -33,5 +33,9 @@
       <item quantity="one">%d วินาที</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ระยะเวลา"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"วัน"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"เดือน"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"ปี"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"ยืนยัน"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"ถัดไป"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-tl/strings.xml b/wear/compose/compose-material3/src/main/res/values-tl/strings.xml
index 5d1f051..fe20f09 100644
--- a/wear/compose/compose-material3/src/main/res/values-tl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-tl/strings.xml
@@ -33,5 +33,9 @@
       <item quantity="other">%d na Segundo</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Panahon"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Araw"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Buwan"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Taon"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Kumpirmahin"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Susunod"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-tr/strings.xml b/wear/compose/compose-material3/src/main/res/values-tr/strings.xml
index 1a0f669..3ec35be 100644
--- a/wear/compose/compose-material3/src/main/res/values-tr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-tr/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d Saniye</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Aralık"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Onayla"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-uk/strings.xml b/wear/compose/compose-material3/src/main/res/values-uk/strings.xml
index 40e732c..bb037ea 100644
--- a/wear/compose/compose-material3/src/main/res/values-uk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-uk/strings.xml
@@ -39,5 +39,13 @@
       <item quantity="other">%d секунди</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Період"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Підтвердити"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ur/strings.xml b/wear/compose/compose-material3/src/main/res/values-ur/strings.xml
index f15dacf..6f902ab 100644
--- a/wear/compose/compose-material3/src/main/res/values-ur/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ur/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">‏‫‎%d سیکنڈ</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"وقفہ"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"تصدیق کریں"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-uz/strings.xml b/wear/compose/compose-material3/src/main/res/values-uz/strings.xml
index 1025cad..4b1121e 100644
--- a/wear/compose/compose-material3/src/main/res/values-uz/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-uz/strings.xml
@@ -33,5 +33,9 @@
       <item quantity="one">%d soniya</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Oraliq"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Kun"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Oy"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Yil"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Tasdiqlash"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Keyingisi"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-vi/strings.xml b/wear/compose/compose-material3/src/main/res/values-vi/strings.xml
index 6d61d00..3b40f28 100644
--- a/wear/compose/compose-material3/src/main/res/values-vi/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-vi/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d giây</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Khoảng thời gian"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Xác nhận"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml b/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml
index d89158c..14ce25a 100644
--- a/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml
@@ -33,5 +33,9 @@
       <item quantity="one">%d 秒</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"时段"</string>
+    <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"日"</string>
+    <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"月"</string>
+    <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"年"</string>
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"确认"</string>
+    <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"下一个"</string>
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml b/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml
index 68bc1b0..0d30e02 100644
--- a/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d 秒</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"時段"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"確認"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml b/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml
index 067f1dc..3e569a9 100644
--- a/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="one">%d 秒</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"期間"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"確認"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-zu/strings.xml b/wear/compose/compose-material3/src/main/res/values-zu/strings.xml
index 159343b..43167ed 100644
--- a/wear/compose/compose-material3/src/main/res/values-zu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zu/strings.xml
@@ -33,5 +33,13 @@
       <item quantity="other">Imizuzwana engu-%d</item>
     </plurals>
     <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Isikhathi"</string>
+    <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+    <skip />
+    <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+    <skip />
     <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Qinisekisa"</string>
+    <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+    <skip />
 </resources>
diff --git a/wear/compose/compose-material3/src/main/res/values/strings.xml b/wear/compose/compose-material3/src/main/res/values/strings.xml
index 213c3ad..868ca2b 100644
--- a/wear/compose/compose-material3/src/main/res/values/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values/strings.xml
@@ -36,4 +36,8 @@
     <string description="Lets the user know that this picker is to change the value of the year date unit. Appears on the DatePicker component, on top of the year picker. [CHAR_LIMIT=8]" name="wear_m3c_date_picker_year">Year</string>
     <string description="Content description of the confirm button of DatePicker and TimePicker components. It lets the user confirm the date or time selected. [CHAR_LIMIT=NONE]" name="wear_m3c_picker_confirm_button_content_description">Confirm</string>
     <string description="Content description of the next button of DatePicker and TimePicker components. It lets the user to move to the next picker. [CHAR_LIMIT=NONE]" name="wear_m3c_picker_next_button_content_description">Next</string>
-</resources>
\ No newline at end of file
+
+    <string description="A message which is used to indicate that an action failed in a FailureConfirmation [CHAR_LIMIT=12]" name="wear_m3c_confirmation_failure_message">Failed</string>
+    <string description="A message which is used to indicate that an action succeeded in a SuccessConfirmation [CHAR_LIMIT=12]" name="wear_m3c_confirmation_success_message">Success</string>
+    <string description="A message which is used to indicate than the user should continue their action on their phone, used in OpenOnPhone component [CHAR_LIMIT=12]" name="wear_m3c_open_on_phone">Open on phone</string>
+</resources>
diff --git a/wear/compose/compose-navigation/build.gradle b/wear/compose/compose-navigation/build.gradle
index 2e021e1..bb35fa2 100644
--- a/wear/compose/compose-navigation/build.gradle
+++ b/wear/compose/compose-navigation/build.gradle
@@ -31,8 +31,8 @@
 }
 
 dependencies {
-    api("androidx.compose.ui:ui:1.7.0-rc01")
-    api("androidx.compose.runtime:runtime:1.7.0-rc01")
+    api("androidx.compose.ui:ui:1.7.0")
+    api("androidx.compose.runtime:runtime:1.7.0")
     api("androidx.navigation:navigation-runtime:2.6.0")
     api(project(":wear:compose:compose-material"))
     api("androidx.activity:activity-compose:1.7.0")
diff --git a/wear/compose/compose-ui-tooling/build.gradle b/wear/compose/compose-ui-tooling/build.gradle
index d49acb1..5dcfeea 100644
--- a/wear/compose/compose-ui-tooling/build.gradle
+++ b/wear/compose/compose-ui-tooling/build.gradle
@@ -32,7 +32,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.8.1")
-    api("androidx.compose.ui:ui-tooling-preview:1.7.0-rc01")
+    api("androidx.compose.ui:ui-tooling-preview:1.7.0")
 
     implementation(libs.kotlinStdlib)
     implementation("androidx.wear:wear-tooling-preview:1.0.0")
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
index f37b66a..98d2264 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
@@ -253,6 +253,6 @@
     @SuppressWarnings("KotlinInternal")
     private android.support.wearable.complications.ComplicationData asWireComplicationData(
             ComplicationDataTimeline timeline) {
-        return timeline.asWireComplicationData$watchface_complications_data_source_debug();
+        return timeline.asWireComplicationData$watchface_complications_data_source_release();
     }
 }
diff --git a/wear/watchface/watchface-style/build.gradle b/wear/watchface/watchface-style/build.gradle
index 91d5360..333ba3b7 100644
--- a/wear/watchface/watchface-style/build.gradle
+++ b/wear/watchface/watchface-style/build.gradle
@@ -87,7 +87,7 @@
 
 // It makes sure that the apks are generated before the assets are packed.
 afterEvaluate {
-    tasks.named("generateDebugAndroidTestAssets").configure { it.dependsOn(copyApkTaskProvider) }
+    tasks.named("generateReleaseAndroidTestAssets").configure { it.dependsOn(copyApkTaskProvider) }
 }
 
 android {
diff --git a/wear/watchface/watchface-style/src/androidTest/java/androidx/wear/watchface/style/IconWireSizeAndDimensionsTest.kt b/wear/watchface/watchface-style/src/androidTest/java/androidx/wear/watchface/style/IconWireSizeAndDimensionsTest.kt
index 52f34d67..4a7305a 100644
--- a/wear/watchface/watchface-style/src/androidTest/java/androidx/wear/watchface/style/IconWireSizeAndDimensionsTest.kt
+++ b/wear/watchface/watchface-style/src/androidTest/java/androidx/wear/watchface/style/IconWireSizeAndDimensionsTest.kt
@@ -39,7 +39,7 @@
     public fun resource() {
         val wireSizeAndDimensions = testIcon.getWireSizeAndDimensions(context)
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            Truth.assertThat(wireSizeAndDimensions.wireSizeBytes).isEqualTo(673)
+            Truth.assertThat(wireSizeAndDimensions.wireSizeBytes).isEqualTo(547)
         } else {
             Truth.assertThat(wireSizeAndDimensions.wireSizeBytes).isNull()
         }
@@ -71,7 +71,7 @@
 
         val estimate = setting.estimateWireSizeInBytesAndValidateIconDimensions(context, 100, 100)
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            Truth.assertThat(estimate).isEqualTo(708)
+            Truth.assertThat(estimate).isEqualTo(582)
         } else {
             Truth.assertThat(estimate).isEqualTo(35)
         }
@@ -160,7 +160,7 @@
 
         val estimate = setting.estimateWireSizeInBytesAndValidateIconDimensions(context, 100, 100)
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            Truth.assertThat(estimate).isEqualTo(2800)
+            Truth.assertThat(estimate).isEqualTo(2296)
         } else {
             Truth.assertThat(estimate).isEqualTo(108)
         }
@@ -182,7 +182,7 @@
 
         val estimate = setting.estimateWireSizeInBytesAndValidateIconDimensions(context, 100, 100)
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            Truth.assertThat(estimate).isEqualTo(767)
+            Truth.assertThat(estimate).isEqualTo(641)
         } else {
             Truth.assertThat(estimate).isEqualTo(94)
         }
@@ -259,7 +259,7 @@
 
         val estimate = setting.estimateWireSizeInBytesAndValidateIconDimensions(context, 100, 100)
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            Truth.assertThat(estimate).isEqualTo(3592)
+            Truth.assertThat(estimate).isEqualTo(2962)
         } else {
             Truth.assertThat(estimate).isEqualTo(227)
         }
diff --git a/window/window-core/api/current.txt b/window/window-core/api/current.txt
index aa7ee82..2c8a4d2 100644
--- a/window/window-core/api/current.txt
+++ b/window/window-core/api/current.txt
@@ -8,39 +8,68 @@
 
 package androidx.window.core.layout {
 
-  public final class WindowHeightSizeClass {
-    field public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
-    field public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
-    field public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
-    field public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
+  @Deprecated public final class WindowHeightSizeClass {
+    field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+    field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
+    field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+    field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
   }
 
-  public static final class WindowHeightSizeClass.Companion {
+  @Deprecated public static final class WindowHeightSizeClass.Companion {
+    method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getCOMPACT();
+    method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getEXPANDED();
+    method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getMEDIUM();
+    property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+    property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+    property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
   }
 
   public final class WindowSizeClass {
-    method public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
-    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
-    method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
-    method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
-    property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
-    property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+    ctor public WindowSizeClass(float widthDp, float heightDp);
+    ctor public WindowSizeClass(int minWidthDp, int minHeightDp);
+    method @Deprecated public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+    method public int getMinHeightDp();
+    method public int getMinWidthDp();
+    method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
+    method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
+    method public boolean isAtLeast(int widthDp, int heightDp);
+    method public boolean isHeightAtLeast(int heightDp);
+    method public boolean isWidthAtLeast(int widthDp);
+    property public final int minHeightDp;
+    property public final int minWidthDp;
+    property @Deprecated public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
+    property @Deprecated public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+    field public static final java.util.Set<androidx.window.core.layout.WindowSizeClass> BREAKPOINTS_V1;
     field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
+    field public static final int HEIGHT_DP_EXPANDED_LOWER_BOUND = 900; // 0x384
+    field public static final int HEIGHT_DP_MEDIUM_LOWER_BOUND = 480; // 0x1e0
+    field public static final int WIDTH_DP_EXPANDED_LOWER_BOUND = 840; // 0x348
+    field public static final int WIDTH_DP_MEDIUM_LOWER_BOUND = 600; // 0x258
   }
 
   public static final class WindowSizeClass.Companion {
-    method public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
-    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
+    method @Deprecated public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
   }
 
-  public final class WindowWidthSizeClass {
-    field public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
-    field public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
-    field public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
-    field public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+  public final class WindowSizeClassSelectors {
+    method public static androidx.window.core.layout.WindowSizeClass computeWindowSizeClass(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int widthDp, int heightDp);
+    method public static androidx.window.core.layout.WindowSizeClass computeWindowSizeClassPreferHeight(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int widthDp, int heightDp);
   }
 
-  public static final class WindowWidthSizeClass.Companion {
+  @Deprecated public final class WindowWidthSizeClass {
+    field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+    field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
+    field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+    field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+  }
+
+  @Deprecated public static final class WindowWidthSizeClass.Companion {
+    method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getCOMPACT();
+    method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getEXPANDED();
+    method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getMEDIUM();
+    property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+    property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+    property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
   }
 
 }
diff --git a/window/window-core/api/restricted_current.txt b/window/window-core/api/restricted_current.txt
index aa7ee82..2c8a4d2 100644
--- a/window/window-core/api/restricted_current.txt
+++ b/window/window-core/api/restricted_current.txt
@@ -8,39 +8,68 @@
 
 package androidx.window.core.layout {
 
-  public final class WindowHeightSizeClass {
-    field public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
-    field public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
-    field public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
-    field public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
+  @Deprecated public final class WindowHeightSizeClass {
+    field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+    field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
+    field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+    field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
   }
 
-  public static final class WindowHeightSizeClass.Companion {
+  @Deprecated public static final class WindowHeightSizeClass.Companion {
+    method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getCOMPACT();
+    method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getEXPANDED();
+    method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getMEDIUM();
+    property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+    property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+    property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
   }
 
   public final class WindowSizeClass {
-    method public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
-    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
-    method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
-    method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
-    property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
-    property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+    ctor public WindowSizeClass(float widthDp, float heightDp);
+    ctor public WindowSizeClass(int minWidthDp, int minHeightDp);
+    method @Deprecated public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+    method public int getMinHeightDp();
+    method public int getMinWidthDp();
+    method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
+    method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
+    method public boolean isAtLeast(int widthDp, int heightDp);
+    method public boolean isHeightAtLeast(int heightDp);
+    method public boolean isWidthAtLeast(int widthDp);
+    property public final int minHeightDp;
+    property public final int minWidthDp;
+    property @Deprecated public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
+    property @Deprecated public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+    field public static final java.util.Set<androidx.window.core.layout.WindowSizeClass> BREAKPOINTS_V1;
     field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
+    field public static final int HEIGHT_DP_EXPANDED_LOWER_BOUND = 900; // 0x384
+    field public static final int HEIGHT_DP_MEDIUM_LOWER_BOUND = 480; // 0x1e0
+    field public static final int WIDTH_DP_EXPANDED_LOWER_BOUND = 840; // 0x348
+    field public static final int WIDTH_DP_MEDIUM_LOWER_BOUND = 600; // 0x258
   }
 
   public static final class WindowSizeClass.Companion {
-    method public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
-    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
+    method @Deprecated public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
   }
 
-  public final class WindowWidthSizeClass {
-    field public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
-    field public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
-    field public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
-    field public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+  public final class WindowSizeClassSelectors {
+    method public static androidx.window.core.layout.WindowSizeClass computeWindowSizeClass(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int widthDp, int heightDp);
+    method public static androidx.window.core.layout.WindowSizeClass computeWindowSizeClassPreferHeight(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int widthDp, int heightDp);
   }
 
-  public static final class WindowWidthSizeClass.Companion {
+  @Deprecated public final class WindowWidthSizeClass {
+    field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+    field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
+    field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+    field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+  }
+
+  @Deprecated public static final class WindowWidthSizeClass.Companion {
+    method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getCOMPACT();
+    method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getEXPANDED();
+    method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getMEDIUM();
+    property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+    property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+    property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
   }
 
 }
diff --git a/window/window-core/build.gradle b/window/window-core/build.gradle
index a484af0..926941d 100644
--- a/window/window-core/build.gradle
+++ b/window/window-core/build.gradle
@@ -26,12 +26,18 @@
 
 plugins {
     id("AndroidXPlugin")
-    id("com.android.library")
 }
 
 androidXMultiplatform {
     jvm()
-    android()
+    androidLibrary {
+        namespace = "androidx.window.core"
+        withAndroidTestOnDeviceBuilder {
+            it.compilationName = "instrumentedTest"
+            it.defaultSourceSetName = "androidInstrumentedTest"
+            it.sourceSetTreeName = "test"
+        }
+    }
     mac()
     linux()
     ios()
@@ -69,10 +75,6 @@
     enableBinaryCompatibilityValidator = false
 }
 
-android {
-    namespace "androidx.window.core"
-}
-
 androidx {
     name = "WindowManager Core"
     type = LibraryType.PUBLISHED_LIBRARY
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt
index e69a63f..1eccac7 100644
--- a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt
+++ b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt
@@ -16,9 +16,6 @@
 
 package androidx.window.core.layout
 
-import androidx.window.core.layout.WindowHeightSizeClass.Companion.COMPACT
-import androidx.window.core.layout.WindowHeightSizeClass.Companion.EXPANDED
-import androidx.window.core.layout.WindowHeightSizeClass.Companion.MEDIUM
 import kotlin.jvm.JvmField
 
 /**
@@ -27,6 +24,8 @@
  * type. It is possible to have resizeable windows in different device types. The viewport might
  * change from a [COMPACT] all the way to an [EXPANDED] size class.
  */
+@Suppress("DEPRECATION")
+@Deprecated("WindowHeightSizeClass will not be developed further, use WindowSizeClass instead.")
 class WindowHeightSizeClass private constructor(private val rawValue: Int) {
 
     override fun toString(): String {
@@ -56,16 +55,22 @@
 
     companion object {
         /** A bucket to represent a compact height, typical for a phone that is in landscape. */
-        @JvmField val COMPACT: WindowHeightSizeClass = WindowHeightSizeClass(0)
+        @Deprecated("WindowHeightSizeClass not be developed further.")
+        @JvmField
+        val COMPACT: WindowHeightSizeClass = WindowHeightSizeClass(0)
 
         /** A bucket to represent a medium height, typical for a phone in portrait or a tablet. */
-        @JvmField val MEDIUM: WindowHeightSizeClass = WindowHeightSizeClass(1)
+        @Deprecated("WindowHeightSizeClass not be developed further.")
+        @JvmField
+        val MEDIUM: WindowHeightSizeClass = WindowHeightSizeClass(1)
 
         /**
          * A bucket to represent an expanded height window, typical for a large tablet or a desktop
          * form-factor.
          */
-        @JvmField val EXPANDED: WindowHeightSizeClass = WindowHeightSizeClass(2)
+        @Deprecated("WindowHeightSizeClass not be developed further.")
+        @JvmField
+        val EXPANDED: WindowHeightSizeClass = WindowHeightSizeClass(2)
 
         /**
          * Returns a recommended [WindowHeightSizeClass] for the height of a window given the height
@@ -75,6 +80,7 @@
          * @return A recommended size class for the height
          * @throws IllegalArgumentException if the height is negative
          */
+        @Deprecated("WindowHeightSizeClass not be developed further.")
         internal fun compute(dpHeight: Float): WindowHeightSizeClass {
             require(dpHeight >= 0) { "Height must be positive, received $dpHeight" }
             return when {
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt
index 9df0361..43bef20 100644
--- a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt
+++ b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt
@@ -16,30 +16,28 @@
 
 package androidx.window.core.layout
 
-import androidx.window.core.ExperimentalWindowCoreApi
+import kotlin.jvm.JvmField
 import kotlin.jvm.JvmStatic
 
 /**
- * [WindowSizeClass] represents breakpoints for a viewport. The recommended width and height break
- * points are presented through [windowWidthSizeClass] and [windowHeightSizeClass]. Designers should
- * design around the different combinations of width and height buckets. Developers should use the
- * different buckets to specify the layouts. Ideally apps will work well in each bucket and by
- * extension work well across multiple devices. If two devices are in similar buckets they should
- * behave similarly.
+ * [WindowSizeClass] represents breakpoints for a viewport. Designers should design around the
+ * different combinations of width and height buckets. Developers should use the different buckets
+ * to specify the layouts. Ideally apps will work well in each bucket and by extension work well
+ * across multiple devices. If two devices are in similar buckets they should behave similarly.
  *
  * This class is meant to be a common definition that can be shared across different device types.
- * Application developers can use WindowSizeClass to have standard window buckets and design the UI
- * around those buckets. Library developers can use these buckets to create different UI with
+ * Application developers can use [WindowSizeClass] to have standard window buckets and design the
+ * UI around those buckets. Library developers can use these buckets to create different UI with
  * respect to each bucket. This will help with consistency across multiple device types.
  *
  * A library developer use-case can be creating some navigation UI library. For a size class with
- * the [WindowWidthSizeClass.EXPANDED] width it might be more reasonable to have a side navigation.
- * For a [WindowWidthSizeClass.COMPACT] width, a bottom navigation might be a better fit.
+ * the [WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND] width it might be more reasonable to have a
+ * side navigation.
  *
  * An application use-case can be applied for apps that use a list-detail pattern. The app can use
- * the [WindowWidthSizeClass.MEDIUM] to determine if there is enough space to show the list and the
- * detail side by side. If all apps follow this guidance then it will present a very consistent user
- * experience.
+ * the [WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND] to determine if there is enough space to show
+ * the list and the detail side by side. If all apps follow this guidance then it will present a
+ * very consistent user experience.
  *
  * In some cases developers or UI systems may decide to create their own break points. A developer
  * might optimize for a window that is smaller than the supported break points or larger. A UI
@@ -50,13 +48,58 @@
  * @see WindowWidthSizeClass
  * @see WindowHeightSizeClass
  */
-class WindowSizeClass
-private constructor(
+class WindowSizeClass(
+    /** Returns the lower bound for the width of the size class in dp. */
+    val minWidthDp: Int,
+    /** Returns the lower bound for the height of the size class in dp. */
+    val minHeightDp: Int
+) {
+
+    /** A convenience constructor that will truncate to ints. */
+    constructor(widthDp: Float, heightDp: Float) : this(widthDp.toInt(), heightDp.toInt())
+
+    init {
+        require(minWidthDp >= 0) {
+            "Expected minWidthDp to be at least 0, minWidthDp: $minWidthDp."
+        }
+        require(minHeightDp >= 0) {
+            "Expected minHeightDp to be at least 0, minHeightDp: $minHeightDp."
+        }
+    }
+
+    @Suppress("DEPRECATION")
+    @Deprecated("Use either isWidthAtLeast or isAtLeast to check matching bounds.")
     /** Returns the [WindowWidthSizeClass] that corresponds to the widthDp of the window. */
-    val windowWidthSizeClass: WindowWidthSizeClass,
+    val windowWidthSizeClass: WindowWidthSizeClass
+        get() = WindowWidthSizeClass.compute(minWidthDp.toFloat())
+
+    @Suppress("DEPRECATION")
+    @Deprecated("Use either isHeightAtLeast or isAtLeast to check matching bounds.")
     /** Returns the [WindowHeightSizeClass] that corresponds to the heightDp of the window. */
     val windowHeightSizeClass: WindowHeightSizeClass
-) {
+        get() = WindowHeightSizeClass.compute(minHeightDp.toFloat())
+
+    /**
+     * Returns `true` when [widthDp] is greater than or equal to [minWidthDp], `false` otherwise.
+     */
+    fun isWidthAtLeast(widthDp: Int): Boolean {
+        return widthDp >= minWidthDp
+    }
+
+    /**
+     * Returns `true` when [heightDp] is greater than or equal to [minHeightDp], `false` otherwise.
+     */
+    fun isHeightAtLeast(heightDp: Int): Boolean {
+        return heightDp >= minHeightDp
+    }
+
+    /**
+     * Returns `true` when [widthDp] is greater than or equal to [minWidthDp] and [heightDp] is
+     * greater than or equal to [minHeightDp], `false` otherwise.
+     */
+    fun isAtLeast(widthDp: Int, heightDp: Int): Boolean {
+        return isWidthAtLeast(widthDp) && isHeightAtLeast(heightDp)
+    }
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
@@ -64,25 +107,49 @@
 
         other as WindowSizeClass
 
-        if (windowWidthSizeClass != other.windowWidthSizeClass) return false
-        if (windowHeightSizeClass != other.windowHeightSizeClass) return false
+        if (minWidthDp != other.minWidthDp) return false
+        if (minHeightDp != other.minHeightDp) return false
 
         return true
     }
 
     override fun hashCode(): Int {
-        var result = windowWidthSizeClass.hashCode()
-        result = 31 * result + windowHeightSizeClass.hashCode()
+        var result = minWidthDp
+        result = 31 * result + minHeightDp
         return result
     }
 
     override fun toString(): String {
-        return "WindowSizeClass {" +
-            "windowWidthSizeClass=$windowWidthSizeClass, " +
-            "windowHeightSizeClass=$windowHeightSizeClass }"
+        return "WindowSizeClass(minWidthDp=$minWidthDp, minHeightDp=$minHeightDp)"
     }
 
     companion object {
+        /** A lower bound for a size class with Medium width in dp. */
+        const val WIDTH_DP_MEDIUM_LOWER_BOUND = 600
+
+        /** A lower bound for a size class with Expanded width in dp. */
+        const val WIDTH_DP_EXPANDED_LOWER_BOUND = 840
+
+        /** A lower bound for a size class with Medium height in dp. */
+        const val HEIGHT_DP_MEDIUM_LOWER_BOUND = 480
+
+        /** A lower bound for a size class with Expanded height in dp. */
+        const val HEIGHT_DP_EXPANDED_LOWER_BOUND = 900
+
+        private val WIDTH_DP_BREAKPOINTS_V1 =
+            listOf(WIDTH_DP_MEDIUM_LOWER_BOUND, WIDTH_DP_EXPANDED_LOWER_BOUND)
+
+        private val HEIGHT_DP_BREAKPOINTS_V1 =
+            listOf(HEIGHT_DP_MEDIUM_LOWER_BOUND, HEIGHT_DP_EXPANDED_LOWER_BOUND)
+
+        @JvmField
+        val BREAKPOINTS_V1 =
+            WIDTH_DP_BREAKPOINTS_V1.flatMap { widthBp ->
+                    HEIGHT_DP_BREAKPOINTS_V1.map { heightBp ->
+                        WindowSizeClass(minWidthDp = widthBp, minHeightDp = heightBp)
+                    }
+                }
+                .toSet()
 
         /**
          * Computes the recommended [WindowSizeClass] for the given width and height in DP.
@@ -93,28 +160,21 @@
          * @throws IllegalArgumentException if [dpWidth] or [dpHeight] is negative.
          */
         @JvmStatic
+        @Deprecated("Use the constructor instead.")
         fun compute(dpWidth: Float, dpHeight: Float): WindowSizeClass {
-            return WindowSizeClass(
-                WindowWidthSizeClass.compute(dpWidth),
-                WindowHeightSizeClass.compute(dpHeight)
-            )
-        }
-
-        /**
-         * Computes the [WindowSizeClass] for the given width and height in pixels with density.
-         *
-         * @param widthPx width of a window in PX.
-         * @param heightPx height of a window in PX.
-         * @param density density of the display where the window is shown.
-         * @return [WindowSizeClass] that is recommended for the given dimensions.
-         * @throws IllegalArgumentException if [widthPx], [heightPx], or [density] is negative.
-         */
-        @JvmStatic
-        @ExperimentalWindowCoreApi
-        fun compute(widthPx: Int, heightPx: Int, density: Float): WindowSizeClass {
-            val widthDp = widthPx / density
-            val heightDp = heightPx / density
-            return compute(widthDp, heightDp)
+            val widthDp =
+                when {
+                    dpWidth >= WIDTH_DP_EXPANDED_LOWER_BOUND -> WIDTH_DP_EXPANDED_LOWER_BOUND
+                    dpWidth >= WIDTH_DP_MEDIUM_LOWER_BOUND -> WIDTH_DP_MEDIUM_LOWER_BOUND
+                    else -> 0
+                }
+            val heightDp =
+                when {
+                    dpHeight >= HEIGHT_DP_EXPANDED_LOWER_BOUND -> HEIGHT_DP_EXPANDED_LOWER_BOUND
+                    dpHeight >= HEIGHT_DP_MEDIUM_LOWER_BOUND -> HEIGHT_DP_MEDIUM_LOWER_BOUND
+                    else -> 0
+                }
+            return WindowSizeClass(widthDp, heightDp)
         }
     }
 }
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClassSelectors.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClassSelectors.kt
new file mode 100644
index 0000000..25ef7c0
--- /dev/null
+++ b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClassSelectors.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024 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.
+ */
+
+@file:JvmName("WindowSizeClassSelectors")
+
+package androidx.window.core.layout
+
+import kotlin.jvm.JvmName
+
+/**
+ * Returns the largest [WindowSizeClass] that is within the bounds of ([widthDp], [heightDp]). This
+ * method prefers width and uses max height to break ties. If there is no match a default of
+ * `WindowSizeClass(0,0)` is returned. Examples: Input: Set: `setOf(WindowSizeClass(300, 300),
+ * WindowSizeClass(300, 600)` widthDp: `300` heightDp: `800` Output: `WindowSizeClass(300, 600)`
+ * Input: Set: `setOf(WindowSizeClass(300, 300), WindowSizeClass(300, 600)` widthDp: `300` heightDp:
+ * `400` Output: `WindowSizeClass(300, 300)`
+ *
+ * @param widthDp the width of the window to match a [WindowSizeClass] to.
+ * @param heightDp the height of the window to match a [WindowSizeClass] to.
+ */
+fun Set<WindowSizeClass>.computeWindowSizeClass(widthDp: Int, heightDp: Int): WindowSizeClass {
+    var maxWidth = 0
+    forEach { bucket ->
+        if (bucket.minWidthDp <= widthDp && bucket.minWidthDp > maxWidth) {
+            maxWidth = bucket.minWidthDp
+        }
+    }
+    var match = WindowSizeClass(0, 0)
+    forEach { bucket ->
+        if (
+            bucket.minWidthDp == maxWidth &&
+                bucket.minHeightDp <= heightDp &&
+                match.minHeightDp < bucket.minHeightDp
+        ) {
+            match = bucket
+        }
+    }
+    return match
+}
+
+/**
+ * Returns the largest [WindowSizeClass] that is within the bounds of ([widthDp], [heightDp]). This
+ * method prefers height and uses max width to break ties. If there is no match a default of
+ * `WindowSizeClass(0,0)` is returned. Examples: Input: Set: `setOf(WindowSizeClass(300, 300),
+ * WindowSizeClass(600, 300)` widthDp: `800` heightDp: `300` Output: `WindowSizeClass(600, 300)`
+ * Input: Set: `setOf(WindowSizeClass(300, 300), WindowSizeClass(600, 300)` widthDp: `400` heightDp:
+ * `300` Output: `WindowSizeClass(300, 300)`
+ *
+ * @param widthDp the width of the window to match a [WindowSizeClass] to.
+ * @param heightDp the height of the window to match a [WindowSizeClass] to.
+ */
+fun Set<WindowSizeClass>.computeWindowSizeClassPreferHeight(
+    widthDp: Int,
+    heightDp: Int
+): WindowSizeClass {
+    var maxHeight = 0
+    forEach { bucket ->
+        if (bucket.minHeightDp <= heightDp && bucket.minHeightDp > maxHeight) {
+            maxHeight = bucket.minHeightDp
+        }
+    }
+    var match = WindowSizeClass(0, 0)
+    forEach { bucket ->
+        if (
+            bucket.minHeightDp == maxHeight &&
+                bucket.minWidthDp <= widthDp &&
+                match.minWidthDp < bucket.minWidthDp
+        ) {
+            match = bucket
+        }
+    }
+    return match
+}
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt
index 11af3a0..3097d8f 100644
--- a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt
+++ b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt
@@ -24,7 +24,10 @@
  * type. It is possible to have resizeable windows in different device types. The viewport might
  * change from a [COMPACT] all the way to an [EXPANDED] size class.
  */
+@Suppress("DEPRECATION")
+@Deprecated("WindowWidthSizeClass will not be developed further, use WindowSizeClass instead.")
 class WindowWidthSizeClass private constructor(private val rawValue: Int) {
+
     override fun toString(): String {
         val name =
             when (this) {
@@ -52,19 +55,25 @@
 
     companion object {
         /** A bucket to represent a compact width window, typical for a phone in portrait. */
-        @JvmField val COMPACT: WindowWidthSizeClass = WindowWidthSizeClass(0)
+        @Deprecated("WindowWidthSizeClass not be developed further.")
+        @JvmField
+        val COMPACT: WindowWidthSizeClass = WindowWidthSizeClass(0)
 
         /**
          * A bucket to represent a medium width window, typical for a phone in landscape or a
          * tablet.
          */
-        @JvmField val MEDIUM: WindowWidthSizeClass = WindowWidthSizeClass(1)
+        @Deprecated("WindowWidthSizeClass not be developed further.")
+        @JvmField
+        val MEDIUM: WindowWidthSizeClass = WindowWidthSizeClass(1)
 
         /**
          * A bucket to represent an expanded width window, typical for a large tablet or desktop
          * form-factor.
          */
-        @JvmField val EXPANDED: WindowWidthSizeClass = WindowWidthSizeClass(2)
+        @Deprecated("WindowWidthSizeClass not be developed further.")
+        @JvmField
+        val EXPANDED: WindowWidthSizeClass = WindowWidthSizeClass(2)
 
         /**
          * Returns a recommended [WindowWidthSizeClass] for the width of a window given the width in
@@ -74,6 +83,7 @@
          * @return A recommended size class for the width
          * @throws IllegalArgumentException if the width is negative
          */
+        @Deprecated("WindowWidthSizeClass not be developed further.")
         internal fun compute(dpWidth: Float): WindowWidthSizeClass {
             require(dpWidth >= 0) { "Width must be positive, received $dpWidth" }
             return when {
diff --git a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowHeightSizeClassTest.kt b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowHeightSizeClassTest.kt
index 80868cf..7183670 100644
--- a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowHeightSizeClassTest.kt
+++ b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowHeightSizeClassTest.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:Suppress("DEPRECATION")
+
 package androidx.window.core.layout
 
 import androidx.window.core.layout.WindowHeightSizeClass.Companion.COMPACT
diff --git a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassSelectorsTest.kt b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassSelectorsTest.kt
new file mode 100644
index 0000000..f72e378
--- /dev/null
+++ b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassSelectorsTest.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2024 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.window.core.layout
+
+import androidx.window.core.layout.WindowSizeClass.Companion.HEIGHT_DP_MEDIUM_LOWER_BOUND
+import androidx.window.core.layout.WindowSizeClass.Companion.WIDTH_DP_MEDIUM_LOWER_BOUND
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class WindowSizeClassSelectorsTest {
+
+    val coreSet = WindowSizeClass.BREAKPOINTS_V1
+
+    @Test
+    fun compute_window_size_class_returns_zero_for_default() {
+        // coreSet does not contain 10, 10
+        val actual = coreSet.computeWindowSizeClass(10, 10)
+
+        assertEquals(WindowSizeClass(0, 0), actual)
+    }
+
+    @Test
+    fun compute_window_size_class_returns_exact_match() {
+        val expected = WindowSizeClass(WIDTH_DP_MEDIUM_LOWER_BOUND, HEIGHT_DP_MEDIUM_LOWER_BOUND)
+
+        // coreSet contains WindowSizeClass(MEDIUM, MEDIUM)
+        val actual =
+            coreSet.computeWindowSizeClass(
+                WIDTH_DP_MEDIUM_LOWER_BOUND,
+                HEIGHT_DP_MEDIUM_LOWER_BOUND
+            )
+
+        assertEquals(expected, actual)
+    }
+
+    @Test
+    fun compute_window_size_class_returns_bounded_match() {
+        val expected = WindowSizeClass(WIDTH_DP_MEDIUM_LOWER_BOUND, HEIGHT_DP_MEDIUM_LOWER_BOUND)
+
+        // coreSet contains WindowSizeClass(MEDIUM, MEDIUM)
+        val actual =
+            coreSet.computeWindowSizeClass(
+                WIDTH_DP_MEDIUM_LOWER_BOUND + 1,
+                HEIGHT_DP_MEDIUM_LOWER_BOUND + 1
+            )
+
+        assertEquals(expected, actual)
+    }
+
+    @Test
+    fun compute_window_size_class_prefers_width() {
+        val expected = WindowSizeClass(minWidthDp = 100, minHeightDp = 50)
+
+        val actual =
+            setOf(
+                    WindowSizeClass(minWidthDp = 100, minHeightDp = 50),
+                    WindowSizeClass(minWidthDp = 50, minHeightDp = 100)
+                )
+                .computeWindowSizeClass(100, 100)
+
+        assertEquals(expected, actual)
+    }
+
+    @Test
+    fun compute_window_size_class_breaks_tie_with_height() {
+        val expected = WindowSizeClass(minWidthDp = 100, minHeightDp = 100)
+
+        val actual =
+            setOf(
+                    WindowSizeClass(minWidthDp = 100, minHeightDp = 50),
+                    WindowSizeClass(minWidthDp = 100, minHeightDp = 100)
+                )
+                .computeWindowSizeClass(200, 200)
+
+        assertEquals(expected, actual)
+    }
+
+    @Test
+    fun compute_window_size_class_preferring_height_returns_zero_for_default() {
+        // coreSet does not contain 10, 10
+        val actual = coreSet.computeWindowSizeClassPreferHeight(10, 10)
+
+        assertEquals(WindowSizeClass(0, 0), actual)
+    }
+
+    @Test
+    fun compute_window_size_class_preferring_height_returns_exact_match() {
+        val expected = WindowSizeClass(WIDTH_DP_MEDIUM_LOWER_BOUND, HEIGHT_DP_MEDIUM_LOWER_BOUND)
+
+        // coreSet contains WindowSizeClass(MEDIUM, MEDIUM)
+        val actual =
+            coreSet.computeWindowSizeClassPreferHeight(
+                WIDTH_DP_MEDIUM_LOWER_BOUND,
+                HEIGHT_DP_MEDIUM_LOWER_BOUND
+            )
+
+        assertEquals(expected, actual)
+    }
+
+    @Test
+    fun compute_window_size_class_preferring_height_returns_bounded_match() {
+        val expected = WindowSizeClass(WIDTH_DP_MEDIUM_LOWER_BOUND, HEIGHT_DP_MEDIUM_LOWER_BOUND)
+
+        // coreSet contains WindowSizeClass(MEDIUM, MEDIUM)
+        val actual =
+            coreSet.computeWindowSizeClassPreferHeight(
+                WIDTH_DP_MEDIUM_LOWER_BOUND + 1,
+                HEIGHT_DP_MEDIUM_LOWER_BOUND + 1
+            )
+
+        assertEquals(expected, actual)
+    }
+
+    @Test
+    fun compute_window_size_class_preferring_height_prefers_height() {
+        val expected = WindowSizeClass(minWidthDp = 50, minHeightDp = 100)
+
+        val actual =
+            setOf(
+                    WindowSizeClass(minWidthDp = 100, minHeightDp = 50),
+                    WindowSizeClass(minWidthDp = 50, minHeightDp = 100)
+                )
+                .computeWindowSizeClassPreferHeight(100, 100)
+
+        assertEquals(expected, actual)
+    }
+
+    @Test
+    fun compute_window_size_class_preferring_height_breaks_tie_with_width() {
+        val expected = WindowSizeClass(minWidthDp = 100, minHeightDp = 100)
+
+        val actual =
+            setOf(
+                    WindowSizeClass(minWidthDp = 50, minHeightDp = 100),
+                    WindowSizeClass(minWidthDp = 100, minHeightDp = 100)
+                )
+                .computeWindowSizeClass(200, 200)
+
+        assertEquals(expected, actual)
+    }
+}
diff --git a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt
index 46607ab..816e40c 100644
--- a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt
+++ b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt
@@ -16,16 +16,18 @@
 
 package androidx.window.core.layout
 
-import androidx.window.core.ExperimentalWindowCoreApi
 import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
 
 /** Tests for [WindowSizeClass] that verify construction. */
 class WindowSizeClassTest {
 
+    @Suppress("DEPRECATION")
     @Test
-    fun testWidthSizeClass_construction() {
+    fun testWindowWidthSizeClass_compatibility() {
         val expected =
             listOf(
                 WindowWidthSizeClass.COMPACT,
@@ -41,6 +43,7 @@
         assertEquals(expected, actual)
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun testWindowSizeClass_computeRounds() {
         val expected = WindowSizeClass.compute(0f, 0f)
@@ -50,18 +53,9 @@
         assertEquals(expected, actual)
     }
 
-    @OptIn(ExperimentalWindowCoreApi::class)
+    @Suppress("DEPRECATION")
     @Test
-    fun testConstruction_usingPx() {
-        val expected = WindowSizeClass.compute(600f, 600f)
-
-        val actual = WindowSizeClass.compute(600, 600, 1f)
-
-        assertEquals(expected, actual)
-    }
-
-    @Test
-    fun testHeightSizeClass_construction() {
+    fun testWindowHeightSizeClass_compatibility() {
         val expected =
             listOf(
                 WindowHeightSizeClass.COMPACT,
@@ -79,16 +73,17 @@
 
     @Test
     fun testEqualsImpliesHashCode() {
-        val first = WindowSizeClass.compute(100f, 500f)
-        val second = WindowSizeClass.compute(100f, 500f)
+        val first = WindowSizeClass(100, 500)
+        val second = WindowSizeClass(100, 500)
 
         assertEquals(first, second)
         assertEquals(first.hashCode(), second.hashCode())
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun truncated_float_does_not_throw() {
-        val sizeClass = WindowSizeClass.compute(0.5f, 0.5f)
+        val sizeClass = WindowSizeClass(0.5f, 0.5f)
 
         val widthSizeClass = sizeClass.windowWidthSizeClass
         val heightSizeClass = sizeClass.windowHeightSizeClass
@@ -97,9 +92,10 @@
         assertEquals(WindowHeightSizeClass.COMPACT, heightSizeClass)
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun zero_size_class_does_not_throw() {
-        val sizeClass = WindowSizeClass.compute(0f, 0f)
+        val sizeClass = WindowSizeClass(0, 0)
 
         val widthSizeClass = sizeClass.windowWidthSizeClass
         val heightSizeClass = sizeClass.windowHeightSizeClass
@@ -110,11 +106,94 @@
 
     @Test
     fun negative_width_throws() {
-        assertFailsWith(IllegalArgumentException::class) { WindowSizeClass.compute(-1f, 0f) }
+        assertFailsWith(IllegalArgumentException::class) { WindowSizeClass(-1, 0) }
     }
 
     @Test
     fun negative_height_throws() {
-        assertFailsWith(IllegalArgumentException::class) { WindowSizeClass.compute(0f, -1f) }
+        assertFailsWith(IllegalArgumentException::class) { WindowSizeClass(0, -1) }
+    }
+
+    @Test
+    fun is_width_at_least_returns_true_when_input_is_greater() {
+        val width = 200
+        val height = 100
+        val sizeClass = WindowSizeClass(width, height)
+
+        assertTrue(sizeClass.isWidthAtLeast(width + 1))
+    }
+
+    @Test
+    fun is_width_at_least_returns_true_when_input_is_equal() {
+        val width = 200
+        val height = 100
+        val sizeClass = WindowSizeClass(width, height)
+
+        assertTrue(sizeClass.isWidthAtLeast(width))
+    }
+
+    @Test
+    fun is_width_at_least_returns_false_when_input_is_smaller() {
+        val width = 200
+        val height = 100
+        val sizeClass = WindowSizeClass(width, height)
+
+        assertFalse(sizeClass.isWidthAtLeast(width - 1))
+    }
+
+    @Test
+    fun is_height_at_least_returns_true_when_input_is_greater() {
+        val width = 200
+        val height = 100
+        val sizeClass = WindowSizeClass(width, height)
+
+        assertTrue(sizeClass.isHeightAtLeast(height + 1))
+    }
+
+    @Test
+    fun is_height_at_least_returns_true_when_input_is_equal() {
+        val width = 200
+        val height = 100
+        val sizeClass = WindowSizeClass(width, height)
+
+        assertTrue(sizeClass.isHeightAtLeast(height))
+    }
+
+    @Test
+    fun is_height_at_least_returns_false_when_input_is_smaller() {
+        val width = 200
+        val height = 100
+        val sizeClass = WindowSizeClass(width, height)
+
+        assertFalse(sizeClass.isHeightAtLeast(height - 1))
+    }
+
+    @Test
+    fun is_at_least_returns_true_when_input_is_greater() {
+        val width = 200
+        val height = 100
+        val sizeClass = WindowSizeClass(width, height)
+
+        assertTrue(sizeClass.isAtLeast(width, height + 1))
+        assertTrue(sizeClass.isAtLeast(width + 1, height))
+    }
+
+    @Test
+    fun is_at_least_returns_true_when_input_is_equal() {
+        val width = 200
+        val height = 100
+        val sizeClass = WindowSizeClass(width, height)
+
+        assertTrue(sizeClass.isAtLeast(width, height))
+    }
+
+    @Test
+    fun is_at_least_returns_false_when_input_is_smaller() {
+        val width = 200
+        val height = 100
+        val sizeClass = WindowSizeClass(width, height)
+
+        assertFalse(sizeClass.isAtLeast(width, height - 1))
+        assertFalse(sizeClass.isAtLeast(width - 1, height))
     }
 }
diff --git a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowWidthSizeClassTest.kt b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowWidthSizeClassTest.kt
index 85ab99b..520b9bf 100644
--- a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowWidthSizeClassTest.kt
+++ b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowWidthSizeClassTest.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:Suppress("DEPRECATION")
+
 package androidx.window.core.layout
 
 import androidx.window.core.layout.WindowWidthSizeClass.Companion.COMPACT
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateScreen.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateScreen.kt
index 92e85eb..3ff2766 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateScreen.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateScreen.kt
@@ -154,7 +154,7 @@
                 activityDisplayBounds = Rect(0, 0, 960, 2142),
             ),
         )
-    DemoTheme { WindowStateScreen(viewModel = WindowStateViewModel(windowStates)) }
+    DemoTheme { WindowStateScreen(viewModel = viewModel { WindowStateViewModel(windowStates) }) }
 }
 
 /**