Merge "Switch to using Room 2.2.2." into androidx-master-dev
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index b29af56..71c7a23 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -118,5 +118,5 @@
     val VIEWPAGER2 = Version("1.1.0-alpha01")
     val WEAR = Version("1.1.0-alpha01")
     val WEBKIT = Version("1.2.0-alpha01")
-    val WORK = Version("2.3.0-beta01")
+    val WORK = Version("2.3.0-rc01")
 }
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
index 26ef9ce..39d9c39 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
@@ -16,7 +16,7 @@
 
 package androidx.camera.camera2;
 
-import static androidx.camera.core.PreviewSurfaceProviders.createSurfaceTextureProvider;
+import static androidx.camera.testing.SurfaceTextureProvider.createSurfaceTextureProvider;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -38,10 +38,10 @@
 import androidx.camera.core.CaptureConfig;
 import androidx.camera.core.LensFacing;
 import androidx.camera.core.Preview;
-import androidx.camera.core.PreviewSurfaceProviders;
 import androidx.camera.core.SessionConfig;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.testing.CameraUtil;
+import androidx.camera.testing.SurfaceTextureProvider;
 import androidx.camera.testing.fakes.FakeCameraControl;
 import androidx.camera.testing.fakes.FakeLifecycleOwner;
 import androidx.core.util.Preconditions;
@@ -85,7 +85,7 @@
     private Semaphore mSurfaceFutureSemaphore;
     private Semaphore mSaveToReleaseSemaphore;
     private Preview.PreviewSurfaceProvider mPreviewSurfaceProviderWithFrameAvailableListener =
-            createSurfaceTextureProvider(new PreviewSurfaceProviders.SurfaceTextureCallback() {
+            createSurfaceTextureProvider(new SurfaceTextureProvider.SurfaceTextureCallback() {
                 @Override
                 public void onSurfaceTextureReady(@NonNull SurfaceTexture surfaceTexture,
                         @NonNull Size resolution) {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
index 6af4ecb..be652ac 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
@@ -16,7 +16,7 @@
 
 package androidx.camera.camera2;
 
-import static androidx.camera.core.PreviewSurfaceProviders.createSurfaceTextureProvider;
+import static androidx.camera.testing.SurfaceTextureProvider.createSurfaceTextureProvider;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -36,10 +36,10 @@
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.LensFacing;
 import androidx.camera.core.Preview;
-import androidx.camera.core.PreviewSurfaceProviders;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.testing.CameraUtil;
 import androidx.camera.testing.GLUtil;
+import androidx.camera.testing.SurfaceTextureProvider;
 import androidx.camera.testing.fakes.FakeLifecycleOwner;
 import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.Observer;
@@ -119,7 +119,7 @@
         initImageCapture();
         mInstrumentation.runOnMainSync(() -> {
             mPreview.setPreviewSurfaceProvider(createSurfaceTextureProvider(
-                    new PreviewSurfaceProviders.SurfaceTextureCallback() {
+                    new SurfaceTextureProvider.SurfaceTextureCallback() {
                         @Override
                         public void onSurfaceTextureReady(@NonNull SurfaceTexture surfaceTexture,
                                 @NonNull Size resolution) {
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/ProcessingSurfaceTextureTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/ProcessingSurfaceTextureTest.java
index 7a0e0c5..c02775d 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/ProcessingSurfaceTextureTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/ProcessingSurfaceTextureTest.java
@@ -16,7 +16,7 @@
 
 package androidx.camera.core;
 
-import static androidx.camera.core.PreviewSurfaceProviders.createSurfaceTextureProvider;
+import static androidx.camera.testing.SurfaceTextureProvider.createSurfaceTextureProvider;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -32,6 +32,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+import androidx.camera.testing.SurfaceTextureProvider;
 import androidx.camera.testing.fakes.FakeCameraCaptureResult;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
@@ -60,7 +61,7 @@
             new CallbackDeferrableSurface(RESOLUTION,
                     CameraXExecutors.directExecutor(),
                     createSurfaceTextureProvider(
-                            new PreviewSurfaceProviders.SurfaceTextureCallback() {
+                            new SurfaceTextureProvider.SurfaceTextureCallback() {
                                 @Override
                                 public void onSurfaceTextureReady(
                                         @NonNull SurfaceTexture surfaceTexture,
@@ -146,7 +147,7 @@
         ProcessingSurface processingSurface = createProcessingSurfaceTexture(
                 new CallbackDeferrableSurface(RESOLUTION, CameraXExecutors.directExecutor(),
                         createSurfaceTextureProvider(
-                                new PreviewSurfaceProviders.SurfaceTextureCallback() {
+                                new SurfaceTextureProvider.SurfaceTextureCallback() {
                                     @Override
                                     public void onSurfaceTextureReady(
                                             @NonNull SurfaceTexture surfaceTexture,
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index 5e6a6c8..c202cf0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -1830,6 +1830,11 @@
          *
          * <p>This method will remove any value set by setTargetAspectRatio().
          *
+         * <p>setTargetAspectRatioCustom will crop the file outputs from takePicture methods
+         * as opposed to {@link #setTargetAspectRatio(int)} which does not apply a crop.  The source
+         * (pre-crop) resolution will an automatically selected resolution suitable for still
+         * images.
+         *
          * <p>For ImageCapture, the outputs are the {@link ImageProxy} or the File passed to image
          * capture listeners.
          *
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 831fdc9..e9e6770 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -235,11 +235,6 @@
      * <p> Setting the callback will signal to the camera that the use case is ready to receive
      * data.
      *
-     * <p> To displaying preview with a {@link TextureView}, consider using
-     * {@link PreviewSurfaceProviders#createSurfaceTextureProvider(
-     *PreviewSurfaceProviders.SurfaceTextureCallback)}
-     * to create the callback.
-     *
      * @return the last set callback or {@code null} if no listener is set
      */
     @UiThread
@@ -255,13 +250,6 @@
      * <p> Setting the provider will signal to the camera that the use case is ready to receive
      * data.
      *
-     * TODO(b/144386349) Remove after minimal version of PreviewView implemented since using with
-     * TextureView is not safe.
-     * <p> To display a preview with a {@link TextureView}, consider using
-     * {@link PreviewSurfaceProviders#createSurfaceTextureProvider(
-     *PreviewSurfaceProviders.SurfaceTextureCallback)}
-     * to create the provider.
-     *
      * @param executor               on which the previewSurfaceProvider will be triggered.
      * @param previewSurfaceProvider PreviewSurfaceProvider that provides a Preview.
      */
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java
index 1fccea4..9452508 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java
@@ -16,7 +16,7 @@
 
 package androidx.camera.extensions;
 
-import static androidx.camera.core.PreviewSurfaceProviders.createSurfaceTextureProvider;
+import static androidx.camera.testing.SurfaceTextureProvider.createSurfaceTextureProvider;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -53,11 +53,11 @@
 import androidx.camera.core.LensFacing;
 import androidx.camera.core.Preview;
 import androidx.camera.core.PreviewConfig;
-import androidx.camera.core.PreviewSurfaceProviders;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.extensions.ExtensionsManager.EffectMode;
 import androidx.camera.extensions.util.ExtensionsTestUtil;
 import androidx.camera.testing.CameraUtil;
+import androidx.camera.testing.SurfaceTextureProvider;
 import androidx.camera.testing.fakes.FakeLifecycleOwner;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.LargeTest;
@@ -143,7 +143,7 @@
                 () -> {
                     // To set the update listener and Preview will change to active state.
                     preview.setPreviewSurfaceProvider(createSurfaceTextureProvider(
-                            new PreviewSurfaceProviders.SurfaceTextureCallback() {
+                            new SurfaceTextureProvider.SurfaceTextureCallback() {
                                 @Override
                                 public void onSurfaceTextureReady(
                                         @NonNull SurfaceTexture surfaceTexture,
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
index 007b2a8..7f4576c 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
@@ -16,7 +16,7 @@
 
 package androidx.camera.extensions;
 
-import static androidx.camera.core.PreviewSurfaceProviders.createSurfaceTextureProvider;
+import static androidx.camera.testing.SurfaceTextureProvider.createSurfaceTextureProvider;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -50,7 +50,6 @@
 import androidx.camera.core.CameraX;
 import androidx.camera.core.LensFacing;
 import androidx.camera.core.Preview;
-import androidx.camera.core.PreviewSurfaceProviders;
 import androidx.camera.extensions.ExtensionsManager.EffectMode;
 import androidx.camera.extensions.impl.CaptureStageImpl;
 import androidx.camera.extensions.impl.PreviewExtenderImpl;
@@ -58,6 +57,7 @@
 import androidx.camera.extensions.impl.RequestUpdateProcessorImpl;
 import androidx.camera.extensions.util.ExtensionsTestUtil;
 import androidx.camera.testing.CameraUtil;
+import androidx.camera.testing.SurfaceTextureProvider;
 import androidx.camera.testing.fakes.FakeLifecycleOwner;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -85,9 +85,9 @@
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private FakeLifecycleOwner mFakeLifecycle;
 
-    private static final PreviewSurfaceProviders.SurfaceTextureCallback
+    private static final SurfaceTextureProvider.SurfaceTextureCallback
             NO_OP_SURFACE_TEXTURE_CALLBACK =
-            new PreviewSurfaceProviders.SurfaceTextureCallback() {
+            new SurfaceTextureProvider.SurfaceTextureCallback() {
                 @Override
                 public void onSurfaceTextureReady(
                         @NonNull SurfaceTexture surfaceTexture,
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/PreviewSurfaceProviders.java b/camera/camera-testing/src/main/java/androidx/camera/testing/SurfaceTextureProvider.java
similarity index 93%
rename from camera/camera-core/src/main/java/androidx/camera/core/PreviewSurfaceProviders.java
rename to camera/camera-testing/src/main/java/androidx/camera/testing/SurfaceTextureProvider.java
index 371399e..f32946c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/PreviewSurfaceProviders.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/SurfaceTextureProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.camera.core;
+package androidx.camera.testing;
 
 import android.graphics.SurfaceTexture;
 import android.util.Size;
@@ -22,6 +22,7 @@
 import android.view.TextureView;
 
 import androidx.annotation.NonNull;
+import androidx.camera.core.Preview;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.impl.utils.futures.Futures;
 
@@ -29,11 +30,8 @@
  * This class creates implementations of PreviewSurfaceProvider that provide Surfaces that have been
  * pre-configured for specific work flows.
  */
-public final class PreviewSurfaceProviders {
-
-    private static final String TAG = "PreviewSurfaceProviders";
-
-    private PreviewSurfaceProviders() {
+public final class SurfaceTextureProvider {
+    private SurfaceTextureProvider() {
     }
 
     /**
@@ -47,8 +45,8 @@
      * Example:
      *
      * <pre><code>
-     * preview.setPreviewSurfaceProvider(createPreviewSurfaceProvider(
-     *         new PreviewSurfaceProviders.SurfaceTextureCallback() {
+     * preview.setPreviewSurfaceProvider(createSurfaceTextureProvider(
+     *         new SurfaceTextureProvider.SurfaceTextureCallback() {
      *             &#64;Override
      *             public void onSurfaceTextureReady(@NonNull SurfaceTexture surfaceTexture) {
      *                 // Use the SurfaceTexture
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/activity/CameraXTestActivity.java b/camera/camera-testing/src/main/java/androidx/camera/testing/activity/CameraXTestActivity.java
index f55ac70..689f653 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/activity/CameraXTestActivity.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/activity/CameraXTestActivity.java
@@ -17,7 +17,7 @@
 package androidx.camera.testing.activity;
 
 
-import static androidx.camera.core.PreviewSurfaceProviders.createSurfaceTextureProvider;
+import static androidx.camera.testing.SurfaceTextureProvider.createSurfaceTextureProvider;
 
 import android.graphics.SurfaceTexture;
 import android.os.Bundle;
@@ -36,10 +36,10 @@
 import androidx.camera.core.LensFacing;
 import androidx.camera.core.Preview;
 import androidx.camera.core.PreviewConfig;
-import androidx.camera.core.PreviewSurfaceProviders;
 import androidx.camera.core.impl.utils.CameraSelectorUtil;
 import androidx.camera.testing.CameraUtil;
 import androidx.camera.testing.R;
+import androidx.camera.testing.SurfaceTextureProvider;
 import androidx.test.espresso.idling.CountingIdlingResource;
 
 /** An activity which starts CameraX preview for testing. */
@@ -94,7 +94,7 @@
                 .build();
         TextureView textureView = findViewById(R.id.textureView);
         mPreview.setPreviewSurfaceProvider(createSurfaceTextureProvider(
-                new PreviewSurfaceProviders.SurfaceTextureCallback() {
+                new SurfaceTextureProvider.SurfaceTextureCallback() {
                     @Override
                     public void onSurfaceTextureReady(@NonNull SurfaceTexture surfaceTexture,
                             @NonNull Size resolution) {
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewMeteringPointFactoryTest.java b/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewMeteringPointFactoryTest.java
index 58177ef..d27cae8 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewMeteringPointFactoryTest.java
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewMeteringPointFactoryTest.java
@@ -16,7 +16,7 @@
 
 package androidx.camera.view;
 
-import static androidx.camera.core.PreviewSurfaceProviders.createSurfaceTextureProvider;
+import static androidx.camera.testing.SurfaceTextureProvider.createSurfaceTextureProvider;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -42,9 +42,9 @@
 import androidx.camera.core.MeteringPoint;
 import androidx.camera.core.MeteringPointFactory;
 import androidx.camera.core.Preview;
-import androidx.camera.core.PreviewSurfaceProviders;
 import androidx.camera.testing.CameraUtil;
 import androidx.camera.testing.CoreAppTestUtil;
+import androidx.camera.testing.SurfaceTextureProvider;
 import androidx.camera.testing.fakes.FakeActivity;
 import androidx.camera.testing.fakes.FakeLifecycleOwner;
 import androidx.test.core.app.ApplicationProvider;
@@ -203,7 +203,7 @@
         Preview preview = new Preview.Builder().build();
         mInstrumentation.runOnMainSync(() -> {
             preview.setPreviewSurfaceProvider(createSurfaceTextureProvider(
-                    new PreviewSurfaceProviders.SurfaceTextureCallback() {
+                    new SurfaceTextureProvider.SurfaceTextureCallback() {
                         @Override
                         public void onSurfaceTextureReady(@NonNull SurfaceTexture surfaceTexture,
                                 @NonNull Size resolution) {
diff --git a/camera/integration-tests/extensionstestapp/build.gradle b/camera/integration-tests/extensionstestapp/build.gradle
index e92a2f8..9d3dcba 100644
--- a/camera/integration-tests/extensionstestapp/build.gradle
+++ b/camera/integration-tests/extensionstestapp/build.gradle
@@ -42,6 +42,7 @@
     implementation(project(":camera:camera-camera2"))
     implementation(project(":camera:camera-extensions"))
     implementation(project(":camera:camera-lifecycle"))
+    implementation(project(":camera:camera-view"))
     implementation 'androidx.test.espresso:espresso-idling-resource:3.1.0'
     implementation(GUAVA_LISTENABLE_FUTURE)
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.java b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.java
index 619e298..52648c2 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.java
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.java
@@ -16,7 +16,7 @@
 
 package androidx.camera.integration.extensions;
 
-import static androidx.camera.core.PreviewSurfaceProviders.createSurfaceTextureProvider;
+import static androidx.camera.testing.SurfaceTextureProvider.createSurfaceTextureProvider;
 
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertNotNull;
@@ -45,7 +45,6 @@
 import androidx.camera.core.LensFacing;
 import androidx.camera.core.Preview;
 import androidx.camera.core.PreviewConfig;
-import androidx.camera.core.PreviewSurfaceProviders;
 import androidx.camera.extensions.AutoImageCaptureExtender;
 import androidx.camera.extensions.AutoPreviewExtender;
 import androidx.camera.extensions.BeautyImageCaptureExtender;
@@ -60,6 +59,7 @@
 import androidx.camera.extensions.NightPreviewExtender;
 import androidx.camera.extensions.PreviewExtender;
 import androidx.camera.testing.GLUtil;
+import androidx.camera.testing.SurfaceTextureProvider;
 import androidx.camera.testing.TimestampCaptureProcessor;
 import androidx.camera.testing.fakes.FakeLifecycleOwner;
 import androidx.test.filters.MediumTest;
@@ -249,7 +249,7 @@
 
         // To set the update listener and Preview will change to active state.
         preview.setPreviewSurfaceProvider(createSurfaceTextureProvider(
-                new PreviewSurfaceProviders.SurfaceTextureCallback() {
+                new SurfaceTextureProvider.SurfaceTextureCallback() {
                     @Override
                     public void onSurfaceTextureReady(@NonNull SurfaceTexture surfaceTexture,
                             @NonNull Size resolution) {
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
index a73a8d3..a4723b4 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
@@ -15,22 +15,16 @@
  */
 package androidx.camera.integration.extensions;
 
-import static androidx.camera.core.PreviewSurfaceProviders.createSurfaceTextureProvider;
-
 import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.graphics.SurfaceTexture;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.StrictMode;
 import android.util.Log;
-import android.util.Size;
-import android.view.TextureView;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.Toast;
 
@@ -43,7 +37,6 @@
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.LensFacing;
 import androidx.camera.core.Preview;
-import androidx.camera.core.PreviewSurfaceProviders;
 import androidx.camera.core.UseCase;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.extensions.AutoImageCaptureExtender;
@@ -59,6 +52,7 @@
 import androidx.camera.extensions.NightImageCaptureExtender;
 import androidx.camera.extensions.NightPreviewExtender;
 import androidx.camera.lifecycle.ProcessCameraProvider;
+import androidx.camera.view.PreviewView;
 import androidx.concurrent.futures.CallbackToFutureAdapter;
 import androidx.core.app.ActivityCompat;
 import androidx.core.content.ContextCompat;
@@ -76,8 +70,6 @@
 import java.util.Calendar;
 import java.util.List;
 import java.util.Locale;
-import java.util.concurrent.Callable;
-import java.util.concurrent.atomic.AtomicReference;
 
 /** An activity that shows off how extensions can be applied */
 public class CameraExtensionsActivity extends AppCompatActivity
@@ -96,24 +88,27 @@
 
     private String mCurrentCameraFacing = "BACK";
 
+    @Nullable
     private Preview mPreview;
+
+    @Nullable
     private ImageCapture mImageCapture;
+
+    @NonNull
     private ImageCaptureType mCurrentImageCaptureType = ImageCaptureType.IMAGE_CAPTURE_TYPE_HDR;
 
     // Espresso testing variables
     @VisibleForTesting
     CountingIdlingResource mTakePictureIdlingResource = new CountingIdlingResource("TakePicture");
-    // Synthetic Accessor
-    @SuppressWarnings("WeakerAccess")
-    TextureView mTextureView;
+
+    private PreviewView mPreviewView;
 
     ProcessCameraProvider mCameraProvider;
 
     /**
      * Creates a preview use case.
      *
-     * <p>This use case observes a {@link SurfaceTexture}. The texture is connected to a {@link
-     * TextureView} to display a camera preview.
+     * <p>This use case uses a {@link PreviewView} to display a camera preview.
      */
     private void createPreview() {
         enablePreview();
@@ -167,26 +162,7 @@
         }
 
         mPreview = builder.build();
-        mPreview.setPreviewSurfaceProvider(createSurfaceTextureProvider(
-                new PreviewSurfaceProviders.SurfaceTextureCallback() {
-                    @Override
-                    public void onSurfaceTextureReady(@NonNull SurfaceTexture surfaceTexture,
-                            @NonNull Size resolution) {
-                        Log.d(TAG, "onSurfaceTextureReady");
-                        // If TextureView was already created, need to re-add it to change the
-                        // SurfaceTexture.
-                        ViewGroup viewGroup = (ViewGroup) mTextureView.getParent();
-                        viewGroup.removeView(mTextureView);
-                        viewGroup.addView(mTextureView);
-                        mTextureView.setSurfaceTexture(surfaceTexture);
-                    }
-
-                    @Override
-                    public void onSafeToRelease(@NonNull SurfaceTexture surfaceTexture) {
-                        Log.d(TAG, "onSafeToRelease");
-                        surfaceTexture.release();
-                    }
-                }));
+        mPreview.setPreviewSurfaceProvider(mPreviewView.getPreviewSurfaceProvider());
     }
 
     enum ImageCaptureType {
@@ -208,44 +184,41 @@
         Button button = findViewById(R.id.PhotoToggle);
         enableImageCapture(mCurrentImageCaptureType);
         button.setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View view) {
-                        disableImageCapture();
-                        // Toggle to next capture type and enable it and set it as current
-                        switch (mCurrentImageCaptureType) {
-                            case IMAGE_CAPTURE_TYPE_HDR:
-                                enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_BOKEH);
-                                enablePreview();
-                                break;
-                            case IMAGE_CAPTURE_TYPE_BOKEH:
-                                enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_NIGHT);
-                                enablePreview();
-                                break;
-                            case IMAGE_CAPTURE_TYPE_NIGHT:
-                                enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_BEAUTY);
-                                enablePreview();
-                                break;
-                            case IMAGE_CAPTURE_TYPE_BEAUTY:
-                                enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_AUTO);
-                                enablePreview();
-                                break;
-                            case IMAGE_CAPTURE_TYPE_AUTO:
-                                enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_DEFAULT);
-                                enablePreview();
-                                break;
-                            case IMAGE_CAPTURE_TYPE_DEFAULT:
-                                enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_NONE);
-                                enablePreview();
-                                break;
-                            case IMAGE_CAPTURE_TYPE_NONE:
-                                enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_HDR);
-                                enablePreview();
-                                break;
-                        }
-                        bindUseCases();
-                        showTakePictureButton();
+                (view) -> {
+                    disableImageCapture();
+                    // Toggle to next capture type and enable it and set it as current
+                    switch (mCurrentImageCaptureType) {
+                        case IMAGE_CAPTURE_TYPE_HDR:
+                            enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_BOKEH);
+                            enablePreview();
+                            break;
+                        case IMAGE_CAPTURE_TYPE_BOKEH:
+                            enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_NIGHT);
+                            enablePreview();
+                            break;
+                        case IMAGE_CAPTURE_TYPE_NIGHT:
+                            enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_BEAUTY);
+                            enablePreview();
+                            break;
+                        case IMAGE_CAPTURE_TYPE_BEAUTY:
+                            enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_AUTO);
+                            enablePreview();
+                            break;
+                        case IMAGE_CAPTURE_TYPE_AUTO:
+                            enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_DEFAULT);
+                            enablePreview();
+                            break;
+                        case IMAGE_CAPTURE_TYPE_DEFAULT:
+                            enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_NONE);
+                            enablePreview();
+                            break;
+                        case IMAGE_CAPTURE_TYPE_NONE:
+                            enableImageCapture(ImageCaptureType.IMAGE_CAPTURE_TYPE_HDR);
+                            enablePreview();
+                            break;
                     }
+                    bindUseCases();
+                    showTakePictureButton();
                 });
 
         Log.i(TAG, "Got UseCase: " + mImageCapture);
@@ -381,7 +354,7 @@
     }
 
     private void bindUseCases() {
-        List<UseCase> useCases = new ArrayList();
+        List<UseCase> useCases = new ArrayList<>();
         // When it is not IMAGE_CAPTURE_TYPE_NONE, mImageCapture won't be null.
         if (mImageCapture != null) {
             useCases.add(mImageCapture);
@@ -408,7 +381,7 @@
         StrictMode.VmPolicy policy =
                 new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build();
         StrictMode.setVmPolicy(policy);
-        mTextureView = findViewById(R.id.textureView);
+        mPreviewView = findViewById(R.id.previewView);
 
         // Get params from adb extra string
         Bundle bundle = getIntent().getExtras();
@@ -537,14 +510,17 @@
         }
     }
 
+    @Nullable
     public Preview getPreview() {
         return mPreview;
     }
 
+    @Nullable
     public ImageCapture getImageCapture() {
         return mImageCapture;
     }
 
+    @NonNull
     public ImageCaptureType getCurrentImageCaptureType() {
         return mCurrentImageCaptureType;
     }
@@ -569,18 +545,4 @@
                 // No-op
         }
     }
-
-    /** A {@link Callable} whose return value can be set. */
-    private static final class SettableCallable<V> implements Callable<V> {
-        private final AtomicReference<V> mValue = new AtomicReference<>();
-
-        public void set(V value) {
-            mValue.set(value);
-        }
-
-        @Override
-        public V call() {
-            return mValue.get();
-        }
-    }
 }
diff --git a/camera/integration-tests/extensionstestapp/src/main/res/layout/activity_camera_extensions.xml b/camera/integration-tests/extensionstestapp/src/main/res/layout/activity_camera_extensions.xml
index 72f8835..46be28e 100644
--- a/camera/integration-tests/extensionstestapp/src/main/res/layout/activity_camera_extensions.xml
+++ b/camera/integration-tests/extensionstestapp/src/main/res/layout/activity_camera_extensions.xml
@@ -24,8 +24,8 @@
     android:layout_height="match_parent"
     tools:context="androidx.camera.integration.extensions.CameraExtensionsActivity">
 
-    <TextureView
-        android:id="@+id/textureView"
+    <androidx.camera.view.PreviewView
+        android:id="@+id/previewView"
         android:layout_width="0dp"
         android:layout_height="0dp"
         app:layout_constraintBottom_toBottomOf="parent"
diff --git a/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/cameracontrollers/CameraXController.kt b/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/cameracontrollers/CameraXController.kt
index a731474..6eae75a 100644
--- a/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/cameracontrollers/CameraXController.kt
+++ b/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/cameracontrollers/CameraXController.kt
@@ -20,7 +20,7 @@
 import android.hardware.camera2.CameraCaptureSession
 import android.hardware.camera2.CameraDevice
 import android.util.Log
-import android.util.Size
+import android.view.Surface
 import android.view.ViewGroup
 import androidx.annotation.experimental.UseExperimental
 import androidx.camera.camera2.Camera2Config
@@ -29,8 +29,6 @@
 import androidx.camera.core.ImageCapture
 import androidx.camera.core.LensFacing
 import androidx.camera.core.Preview
-import androidx.camera.core.PreviewSurfaceProviders
-import androidx.camera.core.PreviewSurfaceProviders.createSurfaceTextureProvider
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
 import androidx.camera.integration.antelope.CameraParams
 import androidx.camera.integration.antelope.CameraXImageAvailableListener
@@ -43,6 +41,7 @@
 import androidx.camera.integration.antelope.TestType
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.lifecycle.LifecycleOwner
+import com.google.common.util.concurrent.Futures
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.guava.await
@@ -103,25 +102,34 @@
 
         // Set preview to observe the surface texture
         activity.runOnUiThread {
-            previewUseCase.previewSurfaceProvider = createSurfaceTextureProvider(
-                object : PreviewSurfaceProviders.SurfaceTextureCallback {
+            previewUseCase.previewSurfaceProvider =
+                Preview.PreviewSurfaceProvider { resolution, surfaceReleaseFuture ->
+                    // Create the SurfaceTexture and Surface
+                    val surfaceTexture = SurfaceTexture(0)
+                    surfaceTexture.setDefaultBufferSize(resolution.width, resolution.height)
+                    surfaceTexture.detachFromGLContext()
+                    val surface = Surface(surfaceTexture)
 
-                    override fun onSafeToRelease(surfaceTexture: SurfaceTexture) {
-                        surfaceTexture.release()
+                    // Attach the SurfaceTexture on the TextureView
+                    if (!isCameraSurfaceTextureReleased(surfaceTexture)) {
+                        val viewGroup = params.cameraXPreviewTexture?.parent as ViewGroup
+                        viewGroup.removeView(params.cameraXPreviewTexture)
+                        viewGroup.addView(params.cameraXPreviewTexture)
+                        params.cameraXPreviewTexture?.surfaceTexture = surfaceTexture
                     }
 
-                    override fun onSurfaceTextureReady(
-                        surfaceTexture: SurfaceTexture,
-                        resolution: Size
-                    ) {
-                        if (!isCameraSurfaceTextureReleased(surfaceTexture)) {
-                            val viewGroup = params.cameraXPreviewTexture?.parent as ViewGroup
-                            viewGroup.removeView(params.cameraXPreviewTexture)
-                            viewGroup.addView(params.cameraXPreviewTexture)
-                            params.cameraXPreviewTexture?.surfaceTexture = surfaceTexture
-                        }
-                    }
-                })
+                    // Release the SurfaceTexture and Surface once camera is done with it
+                    surfaceReleaseFuture.addListener(
+                        Runnable {
+                            surface.release()
+                            surfaceTexture.release()
+                        },
+                        CameraXExecutors.directExecutor()
+                    )
+
+                    // Surface provided to camera for producing buffers into
+                    Futures.immediateFuture(surface)
+                }
         }
 
         // TODO: As of 0.3.0 CameraX can only use front and back cameras.
diff --git a/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTouchEventTest.java b/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTouchEventTest.java
index 6a8e3f0..a98ea78 100644
--- a/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTouchEventTest.java
+++ b/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTouchEventTest.java
@@ -36,7 +36,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.MediumTest;
+import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
@@ -47,7 +47,7 @@
 import org.mockito.InOrder;
 
 @SuppressWarnings({"unchecked", "rawtypes"})
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class CoordinatorLayoutTouchEventTest {
     private static final Touch[] NO_TOUCHES = new Touch[] {};
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/TextBenchmarkHelper.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/TextBenchmarkHelper.kt
index 8e3c77c..8c03978 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/TextBenchmarkHelper.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/TextBenchmarkHelper.kt
@@ -47,7 +47,7 @@
 
     private val metricAffectingTextStyles = arrayOf(
         TextStyle(fontSize = 18.sp),
-        TextStyle(fontSizeScale = 2f),
+        TextStyle(fontSize = 2.em),
         TextStyle(fontWeight = FontWeight.Bold),
         TextStyle(fontStyle = FontStyle.Italic),
         TextStyle(letterSpacing = 0.2.em),
diff --git a/ui/ui-android-text/src/androidTest/java/androidx/text/StaticLayoutCompatTest.java b/ui/ui-android-text/src/androidTest/java/androidx/text/StaticLayoutCompatTest.java
deleted file mode 100644
index cf8cf1e..0000000
--- a/ui/ui-android-text/src/androidTest/java/androidx/text/StaticLayoutCompatTest.java
+++ /dev/null
@@ -1,489 +0,0 @@
-/*
- * Copyright 2018 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.text;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Instrumentation;
-import android.graphics.Typeface;
-import android.text.Layout;
-import android.text.StaticLayout;
-import android.text.TextDirectionHeuristic;
-import android.text.TextDirectionHeuristics;
-import android.text.TextPaint;
-import android.text.TextUtils;
-
-import androidx.test.filters.SdkSuppress;
-import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class StaticLayoutCompatTest {
-    private Typeface mSampleFont;
-
-    @Before
-    public void setUp() {
-        final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        mSampleFont = Typeface.createFromAsset(mInstrumentation.getContext().getAssets(),
-                "sample_font.ttf");
-    }
-
-    @Test
-    public void builder_constructor_returnsGivenParameters() {
-        final CharSequence text = "hello";
-        final TextPaint paint = new TextPaint();
-        final int width = 10;
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, paint, width)
-                .build();
-
-        assertThat(staticLayout.getText()).isEqualTo(text);
-        assertThat(staticLayout.getPaint()).isEqualTo(paint);
-        assertThat(staticLayout.getWidth()).isEqualTo(width);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void builder_constructor_withTextNull_throwsNPE() {
-        new StaticLayoutCompat.Builder(null, new TextPaint(), 0);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void builder_constructor_withPaintNull_throwsNPE() {
-        new StaticLayoutCompat.Builder("", null, 0);
-    }
-
-    @Test
-    public void builder_build_returnNotNull() {
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .build();
-
-        assertThat(staticLayout).isNotNull();
-    }
-
-    @Test
-    public void builder_setText_returnsGivenText() {
-        final CharSequence text = "hello";
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .setText(text)
-                .build();
-
-        assertThat(staticLayout.getText()).isEqualTo(text);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void builder_setText_withNull_throwsNPE() {
-        new StaticLayoutCompat.Builder("", new TextPaint(), 0).setText(null);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void builder_setStart_withNegative_throwsIAE() {
-        new StaticLayoutCompat.Builder("", new TextPaint(), 0).setStart(-1);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void builder_setEnd_withNegative_throwsIAE() {
-        new StaticLayoutCompat.Builder("", new TextPaint(), 0).setEnd(-1);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void builder_setStart_setEnd_withStartGreaterThanEnd_throwsIAE() {
-        new StaticLayoutCompat.Builder("a", new TextPaint(), 0)
-                .setStart(1)
-                .setEnd(0)
-                .build();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void builder_setEnd_withEndGreaterThanLength_throwsIAE() {
-        new StaticLayoutCompat.Builder("a", new TextPaint(), 0)
-                .setEnd("a".length() + 1)
-                .build();
-    }
-
-    @Test
-    public void builder_defaultStart_isZero() {
-        final CharSequence text = "abc";
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, new TextPaint(), 50)
-                .build();
-
-        // Check start is 0
-        assertThat(staticLayout.getLineStart(0)).isEqualTo(0);
-    }
-
-    @Test
-    public void builder_defaultEnd_isTextLength() {
-        final CharSequence str = "abc";
-
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(str, new TextPaint(), 50)
-                .build();
-
-        int lineNum = staticLayout.getLineCount();
-        // Check the end is str.length()
-        assertThat(staticLayout.getLineStart(lineNum)).isEqualTo(str.length());
-    }
-
-    @Test
-    public void builder_setPaint_returnsGivenPaint() {
-        final TextPaint paint = new TextPaint();
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder("", paint, 0)
-                .setPaint(paint)
-                .build();
-
-        assertThat(staticLayout.getPaint()).isEqualTo(paint);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void builder_setPaint_withNull_throwsNPE() {
-        new StaticLayoutCompat.Builder("", new TextPaint(), 0).setPaint(null);
-    }
-
-    @Test
-    public void builder_setWidth_returnsGivenWidth() {
-        final int width = 10;
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .setWidth(width)
-                .build();
-
-        assertThat(staticLayout.getWidth()).isEqualTo(width);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void builder_setWidth_withNegativeInt_throwsIAE() {
-        new StaticLayoutCompat.Builder("", new TextPaint(), 0).setWidth(-1);
-    }
-
-    @Test
-    public void builder_setAlignment_returnsGivenAlignment() {
-        final Layout.Alignment align = Layout.Alignment.ALIGN_OPPOSITE;
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .setAlignment(align)
-                .build();
-
-        assertThat(staticLayout.getAlignment()).isEqualTo(align);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void builder_setAlignment_withNull_throwsNPE() {
-        new StaticLayoutCompat.Builder("", new TextPaint(), 0).setAlignment(null);
-    }
-
-    @Test
-    public void builder_defaultAlignment_returnsAlignNormal() {
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .build();
-
-        assertThat(staticLayout.getAlignment()).isEqualTo(Layout.Alignment.ALIGN_NORMAL);
-    }
-
-    @Test
-    public void builder_setTextDirection_returnsGivenTextDirection() {
-        final TextDirectionHeuristic textDir = TextDirectionHeuristics.RTL;
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .setTextDirection(textDir)
-                .build();
-
-        assertThat(staticLayout.getParagraphDirection(0)).isEqualTo(Layout.DIR_RIGHT_TO_LEFT);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void builder_setTextDirection_withNull_throwsNPE() {
-        new StaticLayoutCompat.Builder("", new TextPaint(), 0).setTextDirection(null);
-    }
-
-    @Test
-    public void builder_defaultTextDirection_withTextStrongLTR_returnLTR() {
-        final CharSequence text = "ab";
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, new TextPaint(), 0)
-                .build();
-
-        assertThat(staticLayout.getParagraphDirection(0)).isEqualTo(Layout.DIR_LEFT_TO_RIGHT);
-    }
-
-    @Test
-    public void builder_defaultTextDirection_withTextStrongRTL_returnRTL() {
-        final CharSequence text = "\u05D0\u05D1";
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, new TextPaint(), 0)
-                .build();
-
-        assertThat(staticLayout.getParagraphDirection(0)).isEqualTo(Layout.DIR_RIGHT_TO_LEFT);
-    }
-
-    @Test
-    public void builder_defaultTextDirection_withoutStrongChar_returnLTR() {
-        final CharSequence text = "..";
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text,
-                new TextPaint(), 0).build();
-
-        assertThat(staticLayout.getParagraphDirection(0)).isEqualTo(Layout.DIR_LEFT_TO_RIGHT);
-    }
-
-    @Test
-    public void builder_setLineSpacingExtra_returnsGivenValues() {
-        final float lineSpacingExtra = 1.0f;
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .setLineSpacingExtra(lineSpacingExtra)
-                .build();
-
-        assertThat(staticLayout.getSpacingAdd()).isEqualTo(lineSpacingExtra);
-    }
-
-    @Test
-    public void builder_setLineSpacingMultiplier_returnsGivenValues() {
-        final float lineSpacingMultiplier = 2.0f;
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .setLineSpacingMultiplier(lineSpacingMultiplier)
-                .build();
-
-        assertThat(staticLayout.getSpacingMultiplier()).isEqualTo(lineSpacingMultiplier);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void builder_setLineSpacingMultiplier_withNegativeFloat_throwsIAE() {
-        new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .setLineSpacingMultiplier(-1.0f)
-                .build();
-    }
-
-    @Test
-    public void builder_defaultLineSpacing_lineSpacingExtraEqualsZero() {
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .build();
-
-        assertThat(staticLayout.getSpacingAdd()).isEqualTo(0.0f);
-    }
-
-    @Test
-    public void builder_defaultLineSpacing_lineSpacingMultiplierEqualsOne() {
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .build();
-
-        assertThat(staticLayout.getSpacingMultiplier()).isEqualTo(1.0f);
-    }
-
-    @Test
-    public void builder_setEllipsize_withMaxLinesEqualsOne_withShortText_isNotEllipsized() {
-        final String text = "abc";
-        final float charWidth = 20.0f;
-        final TextPaint paint = getPaintWithCharWidth(charWidth);
-
-        int width = (int) Math.floor(charWidth * text.length()) + 10;
-        int ellipsizedWidth = width;
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, paint, width)
-                .setEllipsize(TextUtils.TruncateAt.END)
-                .setEllipsizedWidth(ellipsizedWidth)
-                .setMaxLines(1)
-                .build();
-
-        // Ellipsized char in the first line should be zero
-        assertThat(staticLayout.getEllipsisCount(0)).isEqualTo(0);
-    }
-
-    @Test
-    public void builder_setEllipsize_withMaxLinesEqualsOne_withLongText_isEllipsized() {
-        final String text = "abc";
-        final float charWidth = 20.0f;
-        final TextPaint paint = getPaintWithCharWidth(charWidth);
-
-        int width = (int) Math.floor(charWidth * text.length());
-        int ellipsizedWidth = width - 1;
-
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, paint, width)
-                .setEllipsize(TextUtils.TruncateAt.END)
-                .setEllipsizedWidth(ellipsizedWidth)
-                .setMaxLines(1)
-                .build();
-
-        assertThat(staticLayout.getEllipsisCount(0)).isGreaterThan(0);
-    }
-
-    // Testing of BreakStrategy is non-trivial, only test if it will crash.
-    @Test
-    public void builder_setBreakStrategySimple_notCrash() {
-        new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                .build();
-    }
-
-    @Test
-    public void builder_setBreakStrategyHighQuality_notCrash() {
-        new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
-                .build();
-    }
-
-    @Test
-    public void builder_setBreakStrategyBalanced_notCrash() {
-        new StaticLayoutCompat.Builder("", new TextPaint(), 0)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
-                .build();
-    }
-
-    // TODO(haoyuchang): Hyphenation is not working proper before API 28, need to support
-    @Test
-    @SdkSuppress(minSdkVersion = 28)
-    public void builder_setHyphenationFrequencyNone_isNotHyphenated() {
-        final String text = "writing";
-        final float charWidth = 20.0f;
-        final TextPaint paint = getPaintWithCharWidth(charWidth);
-
-        final float width = ("writ".length() + 2) * charWidth;
-
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, paint, (int) width)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
-        // If hyphenation is off, "writing" will become "writin" + "\n" +"g".
-        // The second line should start with 'g'.
-        assertThat(staticLayout.getLineStart(1)).isEqualTo("writin".length());
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 28)
-    public void builder_setHyphenationFrequencyNormal_isHyphenated() {
-        final String text = "writing";
-        final float charWidth = 20.0f;
-        final TextPaint paint = getPaintWithCharWidth(charWidth);
-
-        final float width = ("writ".length() + 2) * charWidth;
-
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, paint, (int) width)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
-                .build();
-
-        assertThat(staticLayout.getLineCount()).isEqualTo(2);
-        // If hyphenation is on, "writing" will become "writ-" + "\n" + "ing".
-        // The second line should start with 'i'.
-        assertThat(staticLayout.getLineStart(1)).isEqualTo("writ".length());
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 28)
-    public void builder_setHyphenationFrequencyFull_isHyphenated() {
-        final String text = "writing";
-        final float charWidth = 20.0f;
-        final TextPaint paint = getPaintWithCharWidth(charWidth);
-
-        final float width = ("writ".length() + 2) * charWidth;
-
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, paint, (int) width)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
-                .build();
-
-        assertThat(staticLayout.getLineCount()).isEqualTo(2);
-        // If hyphenation is on, "writing" will become "writ-" + "\n" + "ing".
-        // The second line should start with 'i'.
-        assertThat(staticLayout.getLineStart(1)).isEqualTo("writ".length());
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 28)
-    public void builder_defaultHyphenationFrequency_isNone() {
-        final String text = "writing";
-        final float charWidth = 20.0f;
-        final TextPaint paint = getPaintWithCharWidth(charWidth);
-
-        final float width = ("writ".length() + 2) * charWidth;
-
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, paint, (int) width)
-                .build();
-
-        assertThat(staticLayout.getLineCount()).isEqualTo(2);
-        // If hyphenation is off, "writing" will become "writin" + "\n" +"g".
-        // The second line should start with 'g'.
-        assertThat(staticLayout.getLineStart(1)).isEqualTo("writin".length());
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 26)
-    public void builder_setJustificationModeNone_noJustification() {
-        final String text = "a b c";
-        final float charWidth = 20.0f;
-        final TextPaint paint = getPaintWithCharWidth(charWidth);
-
-        final float extra = charWidth / 2;
-        final int width = (int) Math.floor("a b".length() * charWidth + extra);
-
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, paint, width)
-                .setJustificationMode(Layout.JUSTIFICATION_MODE_NONE)
-                .build();
-
-        // Last line won't be justified, need two lines.
-        assertThat(staticLayout.getLineCount()).isGreaterThan(1);
-        // If there is no justification, first line width should not be changed.
-        final float firstLineWidth = "a b".length() * charWidth;
-        assertThat(staticLayout.getLineRight(0)).isEqualTo(firstLineWidth);
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 26)
-    public void builder_setJustificationModeInterWord_isJustified() {
-        final String text = "a b c";
-        final float charWidth = 20.0f;
-        final TextPaint paint = getPaintWithCharWidth(charWidth);
-
-        final float extra = charWidth / 2;
-        final int width = (int) Math.floor("a b".length() * charWidth + extra);
-
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, paint, width)
-                .setJustificationMode(Layout.JUSTIFICATION_MODE_INTER_WORD)
-                .build();
-
-        // Last line won't be justified, need two lines.
-        assertThat(staticLayout.getLineCount()).isGreaterThan(1);
-        // The line right much be greater than text length when justification is on.
-        assertThat(staticLayout.getLineRight(0)).isGreaterThan("a b".length() * charWidth);
-        // The line right ideally should be width. But before API 28, justification shows an extra
-        // space at the end of each line. So we tolerate those cases by make sure light right is
-        // bigger than width - sizeOfSpace, where sizeOfSpace equals extra / spaceNum.
-        final int spaceNum = "a b".split(" ").length - 1;
-        final float lineRightLowerBoundary = width - extra / (spaceNum + 1);
-        assertThat(staticLayout.getLineRight(0)).isAtLeast(lineRightLowerBoundary);
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 26)
-    public void builder_defaultJustificationMode_isNone() {
-        final String text = "a b c";
-        final float charWidth = 20.0f;
-        final TextPaint paint = getPaintWithCharWidth(charWidth);
-
-        final float extra = charWidth / 2;
-        final int width = (int) Math.floor("a b".length() * charWidth + extra);
-
-        final StaticLayout staticLayout = new StaticLayoutCompat.Builder(text, paint, width)
-                .build();
-
-        // Last line won't be justified, need two lines.
-        assertThat(staticLayout.getLineCount()).isGreaterThan(1);
-        // If there is no justification, first line width should not be changed.
-        final float firstLineWidth = "a b".length() * charWidth;
-        assertThat(staticLayout.getLineRight(0)).isEqualTo(firstLineWidth);
-    }
-
-    // Returns a paint which render characters with width equals to given charWidth.
-    TextPaint getPaintWithCharWidth(float charWidth) {
-        final TextPaint paint = new TextPaint();
-        paint.setTypeface(mSampleFont);
-        paint.setTextSize(charWidth);
-        return paint;
-    }
-}
diff --git a/ui/ui-android-text/src/androidTest/java/androidx/text/StaticLayoutFactoryTest.kt b/ui/ui-android-text/src/androidTest/java/androidx/text/StaticLayoutFactoryTest.kt
new file mode 100644
index 0000000..6f79444
--- /dev/null
+++ b/ui/ui-android-text/src/androidTest/java/androidx/text/StaticLayoutFactoryTest.kt
@@ -0,0 +1,609 @@
+/*
+ * 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.text
+
+import android.app.Instrumentation
+import android.graphics.Typeface
+import android.text.Layout
+import android.text.TextDirectionHeuristics
+import android.text.TextPaint
+import android.text.TextUtils
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.lang.IllegalArgumentException
+import kotlin.math.floor
+
+@RunWith(JUnit4::class)
+@SmallTest
+class StaticLayoutFactoryTest {
+    private var sampleFont: Typeface? = null
+
+    @Before
+    fun setUp() {
+        val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+        sampleFont = Typeface.createFromAsset(
+            instrumentation.getContext().getAssets(),
+            "sample_font.ttf"
+        )
+    }
+
+    @Test
+    fun create_withText_returnsGiven() {
+        val text = "hello"
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+
+        assertThat(staticLayout.text).isEqualTo(text)
+    }
+
+    @Test
+    fun create_withDefaultStartAndEnd_returnsWholeText() {
+        val text = "ABCDEF"
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+
+        // width Int.MAX_VALUE therefore should be only one line
+        // getLineStart/End will return the index relative to the input text.
+        assertThat(staticLayout.getLineStart(0)).isEqualTo(0)
+        assertThat(staticLayout.getLineEnd(0)).isEqualTo(text.length)
+    }
+
+    @Test
+    fun create_withStartAndEnd_returnsTextInRange() {
+        val text = "ABCDEF"
+        val start = 2
+        val end = 5
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            start = start,
+            end = end,
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+
+        // width Int.MAX_VALUE therefore should be only one line
+        // getLineStart/End will return the index relative to the input text.
+        assertThat(staticLayout.getLineStart(0)).isEqualTo(start)
+        assertThat(staticLayout.getLineEnd(0)).isEqualTo(end)
+    }
+
+    @Test
+    fun create_withPaint_returnsGiven() {
+        val paint = TextPaint().apply { color = 0xFF00FF00.toInt() }
+        val staticLayout = StaticLayoutFactory.create(
+            text = "",
+            paint = paint,
+            width = Int.MAX_VALUE
+        )
+
+        assertThat(staticLayout.paint).isEqualTo(paint)
+    }
+
+    @Test
+    fun create_withWidth_returnsGiven() {
+        val width = 200
+        val staticLayout = StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = width
+        )
+
+        assertThat(staticLayout.width).isEqualTo(width)
+    }
+
+    @Test
+    fun create_withTextDirection_returnsGiven() {
+        val textDir = TextDirectionHeuristics.RTL
+        val staticLayout = StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE,
+            textDir = textDir
+        )
+
+        assertThat(staticLayout.getParagraphDirection(0)).isEqualTo(Layout.DIR_RIGHT_TO_LEFT)
+    }
+
+    @Test
+    fun create_defaultTextDirection_isFirstStrongLTR() {
+        val staticLayoutEmpty = StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+
+        assertThat(staticLayoutEmpty.getParagraphDirection(0))
+            .isEqualTo(Layout.DIR_LEFT_TO_RIGHT)
+
+        val staticLayoutFirstRTL = StaticLayoutFactory.create(
+            text = "\u05D0",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+
+        assertThat(staticLayoutFirstRTL.getParagraphDirection(0))
+            .isEqualTo(Layout.DIR_RIGHT_TO_LEFT)
+    }
+
+    @Test
+    fun create_withAlign_returnsGiven() {
+        val align = Layout.Alignment.ALIGN_OPPOSITE
+        val staticLayout = StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE,
+            alignment = align
+        )
+
+        assertThat(staticLayout.alignment).isEqualTo(align)
+    }
+
+    @Test
+    fun create_defaultAlign_isAlignNormal() {
+        val staticLayout = StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+
+        assertThat(staticLayout.alignment).isEqualTo(Layout.Alignment.ALIGN_NORMAL)
+    }
+
+    @Test
+    fun create_with_ellipsizeEnd_maxLinesOne_shortText_isNotEllipsized() {
+        val text = "abc"
+        val charWidth = 20.0f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val width = floor(charWidth * text.length).toInt() + 10
+        val ellipsizedWidth = width
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = width,
+            ellipsize = TextUtils.TruncateAt.END,
+            ellipsizedWidth = ellipsizedWidth
+        )
+
+        // Ellipsized char in the first line should be zero
+        assertThat(staticLayout.getEllipsisCount(0)).isEqualTo(0)
+    }
+
+    @Test
+    fun create_with_ellipsizeEnd_maxLinesOne_longText_isEllipsized() {
+        val text = "abc"
+        val charWidth = 20.0f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val width = floor(charWidth * text.length).toInt()
+        val ellipsizedWidth = width - 1
+
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = width,
+            ellipsize = TextUtils.TruncateAt.END,
+            ellipsizedWidth = ellipsizedWidth,
+            maxLines = 1
+        )
+
+        assertThat(staticLayout.getEllipsisCount(0)).isGreaterThan(0)
+    }
+
+    @Test
+    fun create_withLineSpacingMultiplier_returnsGiven() {
+        val lineSpacingMultiplier = 1.5f
+        val staticLayout = StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE,
+            lineSpacingMultiplier = lineSpacingMultiplier
+        )
+
+        assertThat(staticLayout.spacingMultiplier).isEqualTo(lineSpacingMultiplier)
+    }
+
+    @Test
+    fun create_defaultLineSpacingMultiplier_isOne() {
+        val staticLayout = StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+
+        assertThat(staticLayout.spacingMultiplier).isEqualTo(1f)
+    }
+
+    @Test
+    fun create_withLineSpacingExtra_returnsGiven() {
+        val lineSpacingExtra = 10f
+        val staticLayout = StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE,
+            lineSpacingExtra = lineSpacingExtra
+        )
+
+        assertThat(staticLayout.spacingAdd).isEqualTo(lineSpacingExtra)
+    }
+
+    @Test
+    fun create_defaultLineSpacingExtra_isZero() {
+        val staticLayout = StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+
+        assertThat(staticLayout.spacingAdd).isZero()
+    }
+
+    @Test
+    fun create_withJustificationModeNone_isNotJustified() {
+        val text = "a b c"
+        val charWidth = 20.0f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val extra = charWidth / 2
+        val width = floor("a b".length * charWidth + extra).toInt()
+
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = width,
+            justificationMode = Layout.JUSTIFICATION_MODE_NONE
+        )
+
+        // Last line won't be justified, need two lines.
+        assertThat(staticLayout.getLineCount()).isGreaterThan(1)
+        // The line right is exactly the space needed by "a b"
+        assertThat(staticLayout.getLineRight(0)).isEqualTo("a b".length * charWidth)
+    }
+
+    @Test
+    fun create_withJustificationModeInterWord_isJustified() {
+        val text = "a b c"
+        val charWidth = 20.0f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val extra = charWidth / 2
+        val width = floor("a b".length * charWidth + extra).toInt()
+
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = width,
+            justificationMode = Layout.JUSTIFICATION_MODE_INTER_WORD
+        )
+
+        // Last line won't be justified, need two lines.
+        assertThat(staticLayout.getLineCount()).isGreaterThan(1)
+        // The line right must be greater than text length when justification is on.
+        assertThat(staticLayout.getLineRight(0)).isGreaterThan("a b".length * charWidth)
+        // The line right ideally should be width. But before API 28, justification shows an extra
+        // space at the end of each line. So we tolerate those cases by make sure light right is
+        // bigger than width - sizeOfSpace, where sizeOfSpace equals extra / spaceNum.
+        val spaceNum = "a b".split(" ").size - 1
+        val lineRightLowerBoundary = width - extra / (spaceNum + 1)
+        assertThat(staticLayout.getLineRight(0)).isAtLeast(lineRightLowerBoundary)
+    }
+
+    @Test
+    fun create_defaultJustificationMode_isNone() {
+        val text = "a b c"
+        val charWidth = 20.0f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val extra = charWidth / 2
+        val width = floor("a b".length * charWidth + extra).toInt()
+
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = width
+        )
+
+        // Last line won't be justified, need two lines.
+        assertThat(staticLayout.getLineCount()).isGreaterThan(1)
+        // The line right is exactly the space needed by "a b"
+        assertThat(staticLayout.getLineRight(0)).isEqualTo("a b".length * charWidth)
+    }
+
+    @Test
+    fun create_withIncludePadding_true() {
+        val text = "a b c"
+        val charWidth = 20f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = Int.MAX_VALUE,
+            includePadding = true
+        )
+
+        val fontMetrics = paint.fontMetricsInt
+        assertThat(staticLayout.height).isEqualTo(fontMetrics.bottom - fontMetrics.top)
+    }
+
+    @Test
+    fun create_withIncludePadding_false() {
+        val text = "a b c"
+        val charWidth = 20f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = Int.MAX_VALUE,
+            includePadding = false
+        )
+
+        val fontMetrics = paint.fontMetricsInt
+            assertThat(staticLayout.height).isEqualTo(fontMetrics.descent - fontMetrics.ascent)
+    }
+
+    @Test
+    fun create_defaultIncludePadding_isTrue() {
+        val text = "a b c"
+        val charWidth = 20f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = Int.MAX_VALUE
+        )
+
+        val fontMetrics = paint.fontMetricsInt
+        assertThat(staticLayout.height).isEqualTo(fontMetrics.bottom - fontMetrics.top)
+    }
+
+    // Testing of BreakStrategy is non-trivial, only test if it will crash.
+    @Test
+    fun create_withBreakStrategySimple_notCrash() {
+        StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = 0,
+            breakStrategy = Layout.BREAK_STRATEGY_SIMPLE
+        )
+    }
+
+    @Test
+    fun create_withBreakStrategyHighQuality_notCrash() {
+        StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = 0,
+            breakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY
+        )
+    }
+
+    @Test
+    fun create_withBreakStrategyBalanced_notCrash() {
+        StaticLayoutFactory.create(
+            text = "",
+            paint = TextPaint(),
+            width = 0,
+            breakStrategy = Layout.BREAK_STRATEGY_BALANCED
+        )
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 28)
+    fun create_withHyphenationFrequencyNone_isNotHyphenated() {
+        val text = "writing"
+        val charWidth = 20.0f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val width = ("writ".length + 2) * charWidth
+
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = width.toInt(),
+            hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE
+        )
+
+        assertThat(staticLayout.getLineCount()).isEqualTo(2)
+        // If hyphenation is off, "writing" will become "writin" + "\n" +"g".
+        // The second line should start with 'g'.
+        assertThat(staticLayout.getLineStart(1)).isEqualTo(text.indexOf('g'))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 28)
+    fun create_withHyphenationFrequencyNormal_isHyphenated() {
+        val text = "writing"
+        val charWidth = 20.0f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val width = ("writ".length + 2) * charWidth
+
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = width.toInt(),
+            hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NORMAL
+        )
+
+        assertThat(staticLayout.getLineCount()).isEqualTo(2)
+        // If hyphenation is on, "writing" will become "writ-" + "\n" + "ing".
+        // The second line should start with second 'i'.
+        assertThat(staticLayout.getLineStart(1)).isEqualTo("writ".length)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 28)
+    fun create_withHyphenationFrequencyFull_isHyphenated() {
+        val text = "writing"
+        val charWidth = 20.0f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val width = ("writ".length + 2) * charWidth
+
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = width.toInt(),
+            hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_FULL
+        )
+
+        assertThat(staticLayout.getLineCount()).isEqualTo(2)
+        // If hyphenation is on, "writing" will become "writ-" + "\n" + "ing".
+        // The second line should start with second 'i'.
+        assertThat(staticLayout.getLineStart(1)).isEqualTo("writ".length)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 28)
+    fun create_defaultHyphenationFrequency_isNone() {
+        val text = "writing"
+        val charWidth = 20.0f
+        val paint = getPaintWithCharWidth(charWidth)
+
+        val width = ("writ".length + 2) * charWidth
+
+        val staticLayout = StaticLayoutFactory.create(
+            text = text,
+            paint = paint,
+            width = width.toInt()
+        )
+
+        assertThat(staticLayout.getLineCount()).isEqualTo(2)
+        // If hyphenation is off, "writing" will become "writin" + "\n" +"g".
+        // The second line should start with 'g'.
+        assertThat(staticLayout.getLineStart(1)).isEqualTo(text.indexOf('g'))
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun create_withStartNegative_throwsIAE() {
+        StaticLayoutFactory.create(
+            text = "abc",
+            start = -1,
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun create_withStartGreaterThanLength_throwsIAE() {
+        StaticLayoutFactory.create(
+            text = "abc",
+            start = "abc".length + 1,
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun create_withEndNegative_throwsIAE() {
+        StaticLayoutFactory.create(
+            text = "abc",
+            end = -1,
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun create_withEndGreaterThanLength_throwsIAE() {
+        StaticLayoutFactory.create(
+            text = "abc",
+            end = "abc".length + 1,
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun create_withStartGreaterThanEnd_throwsIAE() {
+        StaticLayoutFactory.create(
+            text = "abc",
+            start = 2,
+            end = 1,
+            paint = TextPaint(),
+            width = Int.MAX_VALUE
+        )
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun create_withMaxLinesNegative_throwsIAE() {
+        StaticLayoutFactory.create(
+            text = "abc",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE,
+            maxLines = -1
+        )
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun create_withWidthNegative_throwsIAE() {
+        StaticLayoutFactory.create(
+            text = "abc",
+            paint = TextPaint(),
+            width = -1
+        )
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun create_withEllipsizedWidthNegative_throwsIAE() {
+        StaticLayoutFactory.create(
+            text = "abc",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE,
+            ellipsizedWidth = -1
+        )
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun create_withLineSpacingMultiplierNegative_throwsIAE() {
+        StaticLayoutFactory.create(
+            text = "abc",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE,
+            lineSpacingMultiplier = -1f
+        )
+    }
+
+    @Test
+    fun create_withLineSpacingExtraNegative_notCrash() {
+        StaticLayoutFactory.create(
+            text = "abc",
+            paint = TextPaint(),
+            width = Int.MAX_VALUE,
+            lineSpacingExtra = -1f
+        )
+    }
+
+    fun getPaintWithCharWidth(width: Float) = TextPaint().apply {
+        textSize = width
+        typeface = sampleFont
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-android-text/src/main/java/androidx/text/LayoutCompat.java b/ui/ui-android-text/src/main/java/androidx/text/LayoutCompat.java
deleted file mode 100644
index 6bf5c9e..0000000
--- a/ui/ui-android-text/src/main/java/androidx/text/LayoutCompat.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright 2018 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.text;
-
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.text.Layout;
-import android.text.TextDirectionHeuristic;
-import android.text.TextDirectionHeuristics;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.RestrictTo;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * LayoutCompat class which provides all supported attributes by framework, and also defines
- * default value of those attributes for Compose.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class LayoutCompat {
-    private LayoutCompat() { }
-
-    public static final int ALIGN_NORMAL = 0;
-
-    public static final int ALIGN_OPPOSITE = 1;
-
-    public static final int ALIGN_CENTER = 2;
-
-    public static final int ALIGN_LEFT = 3;
-
-    public static final int ALIGN_RIGHT = 4;
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            ALIGN_NORMAL,
-            ALIGN_CENTER,
-            ALIGN_OPPOSITE,
-            ALIGN_LEFT,
-            ALIGN_RIGHT
-    })
-    @interface TextLayoutAlignment { }
-
-    public static final int JUSTIFICATION_MODE_NONE = Layout.JUSTIFICATION_MODE_NONE;
-
-    public static final int JUSTIFICATION_MODE_INTER_WORD = Layout.JUSTIFICATION_MODE_INTER_WORD;
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            JUSTIFICATION_MODE_NONE,
-            JUSTIFICATION_MODE_INTER_WORD
-    })
-    @interface JustificationMode { }
-
-    public static final int HYPHENATION_FREQUENCY_NORMAL = Layout.HYPHENATION_FREQUENCY_NORMAL;
-
-    public static final int HYPHENATION_FREQUENCY_FULL = Layout.HYPHENATION_FREQUENCY_FULL;
-
-    public static final int HYPHENATION_FREQUENCY_NONE = Layout.HYPHENATION_FREQUENCY_NONE;
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            HYPHENATION_FREQUENCY_NORMAL,
-            HYPHENATION_FREQUENCY_FULL,
-            HYPHENATION_FREQUENCY_NONE
-    })
-    @interface HyphenationFrequency { }
-
-    public static final int BREAK_STRATEGY_SIMPLE = Layout.BREAK_STRATEGY_SIMPLE;
-
-    public static final int BREAK_STRATEGY_HIGH_QUALITY = Layout.BREAK_STRATEGY_HIGH_QUALITY;
-
-    public static final int BREAK_STRATEGY_BALANCED = Layout.BREAK_STRATEGY_BALANCED;
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            BREAK_STRATEGY_SIMPLE,
-            BREAK_STRATEGY_HIGH_QUALITY,
-            BREAK_STRATEGY_BALANCED
-    })
-    @interface BreakStrategy { }
-
-    public static final int TEXT_DIRECTION_LTR = 0;
-
-    public static final int TEXT_DIRECTION_RTL = 1;
-
-    public static final int TEXT_DIRECTION_FIRST_STRONG_LTR = 2;
-
-    public static final int TEXT_DIRECTION_FIRST_STRONG_RTL = 3;
-
-    public static final int TEXT_DIRECTION_ANY_RTL_LTR = 4;
-
-    public static final int TEXT_DIRECTION_LOCALE = 5;
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            TEXT_DIRECTION_LTR,
-            TEXT_DIRECTION_RTL,
-            TEXT_DIRECTION_FIRST_STRONG_LTR,
-            TEXT_DIRECTION_FIRST_STRONG_RTL,
-            TEXT_DIRECTION_ANY_RTL_LTR,
-            TEXT_DIRECTION_LOCALE
-    })
-    @interface TextDirection { }
-
-    public static final int DEFAULT_ALIGNMENT = ALIGN_NORMAL;
-
-    public static final int DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_FIRST_STRONG_LTR;
-
-    public static final float DEFAULT_LINESPACING_MULTIPLIER = 1.0f;
-
-    public static final float DEFAULT_LINESPACING_EXTRA = 0.0f;
-
-    public static final boolean DEFAULT_INCLUDE_PADDING = true;
-
-    public static final int DEFAULT_MAX_LINES = Integer.MAX_VALUE;
-
-    public static final int DEFAULT_BREAK_STRATEGY = BREAK_STRATEGY_SIMPLE;
-
-    public static final int DEFAULT_HYPHENATION_FREQUENCY = HYPHENATION_FREQUENCY_NONE;
-
-    public static final int DEFAULT_JUSTIFICATION_MODE = JUSTIFICATION_MODE_NONE;
-
-    static final boolean DEFAULT_ADD_LAST_LINE_LINE_SPACING = false;
-
-    static final boolean DEFAULT_FALLBACK_LINE_SPACING = true;
-
-    static final Layout.Alignment DEFAULT_LAYOUT_ALIGNMENT = Layout.Alignment.ALIGN_NORMAL;
-
-    static final TextDirectionHeuristic DEFAULT_TEXT_DIRECTION_HEURISTIC =
-            TextDirectionHeuristics.FIRSTSTRONG_LTR;
-}
diff --git a/ui/ui-android-text/src/main/java/androidx/text/LayoutCompat.kt b/ui/ui-android-text/src/main/java/androidx/text/LayoutCompat.kt
new file mode 100644
index 0000000..16b2df7
--- /dev/null
+++ b/ui/ui-android-text/src/main/java/androidx/text/LayoutCompat.kt
@@ -0,0 +1,139 @@
+/*
+ * 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.text
+
+import android.graphics.text.LineBreaker
+import android.text.Layout
+import android.text.Layout.Alignment
+import android.text.TextDirectionHeuristic
+import android.text.TextDirectionHeuristics
+import androidx.annotation.IntDef
+import androidx.annotation.RestrictTo
+import androidx.annotation.RestrictTo.Scope
+
+/**
+ * LayoutCompat class which provides all supported attributes by framework, and also defines
+ * default value of those attributes for Compose.
+ *
+ * @hide
+ */
+object LayoutCompat {
+    const val ALIGN_NORMAL = 0
+    const val ALIGN_OPPOSITE = 1
+    const val ALIGN_CENTER = 2
+    const val ALIGN_LEFT = 3
+    const val ALIGN_RIGHT = 4
+    /**
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(
+        ALIGN_NORMAL,
+        ALIGN_CENTER,
+        ALIGN_OPPOSITE,
+        ALIGN_LEFT,
+        ALIGN_RIGHT
+    )
+    internal annotation class TextLayoutAlignment
+
+    const val JUSTIFICATION_MODE_NONE = LineBreaker.JUSTIFICATION_MODE_NONE
+    const val JUSTIFICATION_MODE_INTER_WORD = LineBreaker.JUSTIFICATION_MODE_INTER_WORD
+    /**
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(JUSTIFICATION_MODE_NONE, JUSTIFICATION_MODE_INTER_WORD)
+    internal annotation class JustificationMode
+
+    const val HYPHENATION_FREQUENCY_NORMAL = Layout.HYPHENATION_FREQUENCY_NORMAL
+    const val HYPHENATION_FREQUENCY_FULL = Layout.HYPHENATION_FREQUENCY_FULL
+    const val HYPHENATION_FREQUENCY_NONE = Layout.HYPHENATION_FREQUENCY_NONE
+    /**
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(
+        HYPHENATION_FREQUENCY_NORMAL,
+        HYPHENATION_FREQUENCY_FULL,
+        HYPHENATION_FREQUENCY_NONE
+    )
+    internal annotation class HyphenationFrequency
+
+    const val BREAK_STRATEGY_SIMPLE = LineBreaker.BREAK_STRATEGY_SIMPLE
+    const val BREAK_STRATEGY_HIGH_QUALITY = LineBreaker.BREAK_STRATEGY_HIGH_QUALITY
+    const val BREAK_STRATEGY_BALANCED = LineBreaker.BREAK_STRATEGY_BALANCED
+    /**
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(
+        BREAK_STRATEGY_SIMPLE,
+        BREAK_STRATEGY_HIGH_QUALITY,
+        BREAK_STRATEGY_BALANCED
+    )
+    internal annotation class BreakStrategy
+
+    const val TEXT_DIRECTION_LTR = 0
+    const val TEXT_DIRECTION_RTL = 1
+    const val TEXT_DIRECTION_FIRST_STRONG_LTR = 2
+    const val TEXT_DIRECTION_FIRST_STRONG_RTL = 3
+    const val TEXT_DIRECTION_ANY_RTL_LTR = 4
+    const val TEXT_DIRECTION_LOCALE = 5
+    /**
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(
+        TEXT_DIRECTION_LTR,
+        TEXT_DIRECTION_RTL,
+        TEXT_DIRECTION_FIRST_STRONG_LTR,
+        TEXT_DIRECTION_FIRST_STRONG_RTL,
+        TEXT_DIRECTION_ANY_RTL_LTR,
+        TEXT_DIRECTION_LOCALE
+    )
+    internal annotation class TextDirection
+
+    const val DEFAULT_ALIGNMENT = ALIGN_NORMAL
+
+    const val DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_FIRST_STRONG_LTR
+
+    const val DEFAULT_LINESPACING_MULTIPLIER = 1.0f
+
+    const val DEFAULT_LINESPACING_EXTRA = 0.0f
+
+    const val DEFAULT_INCLUDE_PADDING = true
+
+    const val DEFAULT_MAX_LINES = Integer.MAX_VALUE
+
+    const val DEFAULT_BREAK_STRATEGY = BREAK_STRATEGY_SIMPLE
+
+    const val DEFAULT_HYPHENATION_FREQUENCY = HYPHENATION_FREQUENCY_NONE
+
+    const val DEFAULT_JUSTIFICATION_MODE = JUSTIFICATION_MODE_NONE
+
+    const val DEFAULT_FALLBACK_LINE_SPACING = true
+
+    val DEFAULT_LAYOUT_ALIGNMENT = Alignment.ALIGN_NORMAL
+
+    val DEFAULT_TEXT_DIRECTION_HEURISTIC: TextDirectionHeuristic =
+        TextDirectionHeuristics.FIRSTSTRONG_LTR
+}
\ No newline at end of file
diff --git a/ui/ui-android-text/src/main/java/androidx/text/StaticLayoutCompat.java b/ui/ui-android-text/src/main/java/androidx/text/StaticLayoutCompat.java
deleted file mode 100644
index 0717706..0000000
--- a/ui/ui-android-text/src/main/java/androidx/text/StaticLayoutCompat.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Copyright 2018 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.text;
-
-import static androidx.text.LayoutCompat.DEFAULT_ADD_LAST_LINE_LINE_SPACING;
-import static androidx.text.LayoutCompat.DEFAULT_BREAK_STRATEGY;
-import static androidx.text.LayoutCompat.DEFAULT_FALLBACK_LINE_SPACING;
-import static androidx.text.LayoutCompat.DEFAULT_HYPHENATION_FREQUENCY;
-import static androidx.text.LayoutCompat.DEFAULT_INCLUDE_PADDING;
-import static androidx.text.LayoutCompat.DEFAULT_JUSTIFICATION_MODE;
-import static androidx.text.LayoutCompat.DEFAULT_LAYOUT_ALIGNMENT;
-import static androidx.text.LayoutCompat.DEFAULT_LINESPACING_EXTRA;
-import static androidx.text.LayoutCompat.DEFAULT_LINESPACING_MULTIPLIER;
-import static androidx.text.LayoutCompat.DEFAULT_MAX_LINES;
-import static androidx.text.LayoutCompat.DEFAULT_TEXT_DIRECTION_HEURISTIC;
-
-import android.os.Build;
-import android.text.Layout;
-import android.text.StaticLayout;
-import android.text.TextDirectionHeuristic;
-import android.text.TextDirectionHeuristics;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.FloatRange;
-import androidx.annotation.IntRange;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.text.LayoutCompat.BreakStrategy;
-import androidx.text.LayoutCompat.HyphenationFrequency;
-import androidx.text.LayoutCompat.JustificationMode;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-
-class StaticLayoutCompat {
-    private StaticLayoutCompat() {}
-
-    /**
-     * Builder class for StaticLayout.
-     */
-    public static class Builder {
-        private static final String TAG = "StaticLayoutCompat";
-        private static boolean sIsInitialized = false;
-        private static Constructor sStaticLayoutConstructor = null;
-
-        private static void obtainStaticLayoutConstructor() {
-            if (sIsInitialized) return;
-            sIsInitialized = true;
-            try {
-                sStaticLayoutConstructor = StaticLayout.class.getConstructor(
-                        CharSequence.class,
-                        int.class, /* start */
-                        int.class, /* end */
-                        TextPaint.class,
-                        int.class, /* width */
-                        Layout.Alignment.class,
-                        TextDirectionHeuristic.class,
-                        float.class, /* lineSpacingMultiplier */
-                        float.class, /* lineSpacingExtra */
-                        boolean.class, /* includePadding */
-                        TextUtils.TruncateAt.class,
-                        int.class, /* ellipsizeWidth */
-                        int.class /* maxLines */);
-            } catch (NoSuchMethodException e) {
-                sStaticLayoutConstructor = null;
-                Log.e(TAG, "unable to collect necessary constructor.");
-            }
-        }
-
-        @NonNull
-        private CharSequence mText;
-
-        @IntRange(from = 0)
-        private int mStart;
-
-        @IntRange(from = 0)
-        private int mEnd;
-
-        @NonNull
-        private TextPaint mPaint;
-
-        @IntRange(from = 0)
-        private int mWidth;
-
-        @NonNull
-        private Layout.Alignment mAlignment;
-
-        @NonNull
-        private TextDirectionHeuristic mTextDir;
-
-        private float mLineSpacingExtra;
-
-        @FloatRange(from = 0.0f)
-        float mLineSpacingMultiplier;
-
-        boolean mIncludePadding;
-
-        boolean mFallbackLineSpacing;
-
-        @Nullable
-        TextUtils.TruncateAt mEllipsize;
-
-        @IntRange(from = 0)
-        int mEllipsizedWidth;
-
-        @IntRange(from = 0)
-        int mMaxLines;
-
-        @BreakStrategy
-        int mBreakStrategy;
-
-        @HyphenationFrequency
-        int mHyphenationFrequency;
-
-        @JustificationMode
-        int mJustificationMode;
-
-        @Nullable
-        int[] mLeftIndents;
-
-        @Nullable
-        int[] mRightIndents;
-
-        boolean mAddLastLineLineSpacing;
-
-        /**
-         * Builder for StaticLayout.
-         *
-         * @param text The text to be laid out, optionally with spans
-         * @param paint The base paint used for layout
-         * @param width The width in pixels
-         */
-        public Builder(
-                @NonNull CharSequence text,
-                @NonNull TextPaint paint,
-                @IntRange(from = 0) int width) {
-            mText = Preconditions.checkNotNull(text, "Text can't be null");
-            mStart = 0;
-            mEnd = mText.length();
-            mPaint = Preconditions.checkNotNull(paint, "Paint can't be null");
-            mWidth = Preconditions.checkArgumentNonnegative(width, "Width can't be negative");
-            mAlignment = DEFAULT_LAYOUT_ALIGNMENT;
-            mTextDir = DEFAULT_TEXT_DIRECTION_HEURISTIC;
-            mLineSpacingMultiplier = DEFAULT_LINESPACING_MULTIPLIER;
-            mLineSpacingExtra = DEFAULT_LINESPACING_EXTRA;
-            mIncludePadding = DEFAULT_INCLUDE_PADDING;
-            mFallbackLineSpacing = DEFAULT_FALLBACK_LINE_SPACING;
-            mEllipsize = null;
-            mEllipsizedWidth = width;
-            mMaxLines = DEFAULT_MAX_LINES;
-            mBreakStrategy = DEFAULT_BREAK_STRATEGY;
-            mHyphenationFrequency = DEFAULT_HYPHENATION_FREQUENCY;
-            mJustificationMode = DEFAULT_JUSTIFICATION_MODE;
-            mAddLastLineLineSpacing = DEFAULT_ADD_LAST_LINE_LINE_SPACING;
-        }
-
-        /**
-         * Set the text. Only useful when re-using the builder.
-         *
-         * @param text  The text to be laid out, optionally with spans
-         * @return this builder, useful for chaining
-         */
-        @NonNull
-        public Builder setText(@NonNull CharSequence text) {
-            Preconditions.checkNotNull(text, "Text can't be null");
-            setText(text, 0, text.length());
-            return this;
-        }
-
-        /**
-         * Set the text. Only useful when re-using the builder.
-         *
-         * @param text  The text to be laid out, optionally with spans
-         * @param start The index of the start of the text
-         * @param end   The index + 1 of the end of the text
-         * @return this builder, useful for chaining
-         */
-        @NonNull
-        public Builder setText(@NonNull CharSequence text,
-                @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
-            mText = Preconditions.checkNotNull(text, "Text can't be null");
-            mStart = Preconditions.checkArgumentInRange(start, 0, text.length(), "Start");
-            mEnd = Preconditions.checkArgumentInRange(end, 0, text.length(), "End");
-            return this;
-        }
-
-        /**
-         * Set the start of the text. Should be in range of [0, end],
-         * which is checked in {@link Builder#build}
-         *
-         * @param start The index of the start of the text
-         * @return this builder, useful for chaining
-         */
-        public Builder setStart(@IntRange(from = 0) int start) {
-            mStart = Preconditions.checkArgumentNonnegative(start, "Start can't be negative");
-            return this;
-        }
-
-        /**
-         * Set the end of the text. Should be in range of [start, text.length],
-         * which is checked in {@link Builder#build}.
-         *
-         * @param end The index + 1 of the end of the text.
-         * @return this builder, useful for chaining
-         */
-        public Builder setEnd(@IntRange(from = 0) int end) {
-            mEnd = Preconditions.checkArgumentNonnegative(end, "End can't be negative");
-            return this;
-        }
-
-        /**
-         * Set the paint. Internal for reuse cases only.
-         *
-         * @param paint The base paint used for layout
-         * @return this builder, useful for chaining
-         */
-        @NonNull
-        public Builder setPaint(@NonNull TextPaint paint) {
-            mPaint = Preconditions.checkNotNull(paint, "Paint can't be null");
-            return this;
-        }
-
-        /**
-         * Set the width. Internal for reuse cases only.
-         *
-         * @param width The width in pixels
-         * @return this builder, useful for chaining
-         */
-        @NonNull
-        public Builder setWidth(@IntRange(from = 0) int width) {
-            mWidth = Preconditions.checkArgumentNonnegative(width, "Width can't be negative");
-            return this;
-        }
-
-        /**
-         * Set the alignment. The default is {@link Layout.Alignment#ALIGN_NORMAL}.
-         *
-         * @param alignment Alignment for the resulting {@link StaticLayout}
-         * @return this builder, useful for chaining
-         */
-        @NonNull
-        public Builder setAlignment(@NonNull Layout.Alignment alignment) {
-            mAlignment = Preconditions.checkNotNull(alignment, "Alignment can't be null");
-            return this;
-        }
-
-        /**
-         * Set the text direction heuristic. The text direction heuristic is used to
-         * resolve text direction per-paragraph based on the input text. The default is
-         * {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
-         *
-         * @param textDir text direction heuristic for resolving bidi behavior.
-         * @return this builder, useful for chaining
-         */
-        @NonNull
-        public Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
-            mTextDir = Preconditions.checkNotNull(textDir, "TextDir can't be null");
-            return this;
-        }
-
-        /**
-         * Set line spacing extra. Each line will have its line spacing increased by
-         * {@code lineSpacingExtra}. The default value is 0.0f.
-         *
-         * @param lineSpacingExtra the amount of line spacing addition
-         * @return this builder, useful for chaining
-         * @see android.widget.TextView#setLineSpacing
-         */
-        @NonNull
-        public Builder setLineSpacingExtra(float lineSpacingExtra) {
-            mLineSpacingExtra = lineSpacingExtra;
-            return this;
-        }
-
-        /**
-         * Set line spacing multiplier. Each line will have its line spacing multiplied by
-         * {@code lineSpacingMultiplier}. The default value is 1.0f, and lineSpacingMultiplier
-         * should be non-negative.
-         *
-         * @param lineSpacingMultiplier the line spacing multiplier
-         * @return this builder, useful for chaining
-         * @see android.widget.TextView#setLineSpacing
-         */
-        @NonNull
-        public Builder setLineSpacingMultiplier(
-                @FloatRange(from = 0.0f) float lineSpacingMultiplier) {
-            mLineSpacingMultiplier = Preconditions.checkArgumentInRange(lineSpacingMultiplier,
-                    0.0f, Float.MAX_VALUE, "LineSpacingMultiplier");
-            return this;
-        }
-
-        /**
-         * Set whether to include extra space beyond font ascent and descent (which is
-         * needed to avoid clipping in some languages, such as Arabic and Kannada). The
-         * default is {@code true}.
-         *
-         * @param includePad whether to include padding
-         * @return this builder, useful for chaining
-         * @see android.widget.TextView#setIncludeFontPadding
-         */
-        @NonNull
-        public Builder setIncludePad(boolean includePad) {
-            mIncludePadding = includePad;
-            return this;
-        }
-
-        /**
-         * Set whether to respect the ascent and descent of the fallback fonts that are used in
-         * displaying the text (which is needed to avoid text from consecutive lines running into
-         * each other). If set, fallback fonts that end up getting used can increase the ascent
-         * and descent of the lines that they are used on.
-         *
-         * <p>For backward compatibility reasons, the default is {@code false}, but setting this to
-         * true is strongly recommended. It is required to be true if text could be in languages
-         * like Burmese or Tibetan where text is typically much taller or deeper than Latin text.
-         *
-         * @param useLineSpacingFromFallbacks whether to expand linespacing based on fallback fonts
-         * @return this builder, useful for chaining
-         */
-        @NonNull
-        public Builder setUseLineSpacingFromFallbacks(boolean useLineSpacingFromFallbacks) {
-            mFallbackLineSpacing = useLineSpacingFromFallbacks;
-            return this;
-        }
-
-        /**
-         * Set ellipsizing on the layout. Causes words that are longer than the view
-         * is wide, or exceeding the number of lines (see #setMaxLines) in the case
-         * of {@link android.text.TextUtils.TruncateAt#END} or
-         * {@link android.text.TextUtils.TruncateAt#MARQUEE}, to be ellipsized instead
-         * of broken. The default is {@code null}, indicating no ellipsis is to be applied.
-         *
-         * @param ellipsize type of ellipsis behavior
-         * @return this builder, useful for chaining
-         * @see android.widget.TextView#setEllipsize
-         */
-        @NonNull
-        public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) {
-            mEllipsize = ellipsize;
-            return this;
-        }
-
-        /**
-         * Set the width as used for ellipsizing purposes, if it differs from the
-         * normal layout width. The default is the {@code width}
-         * passed to {@link #Builder(CharSequence, TextPaint, int)}.
-         *
-         * @param ellipsizedWidth width used for ellipsizing, in pixels
-         * @return this builder, useful for chaining
-         * @see android.widget.TextView#setEllipsize
-         */
-        @NonNull
-        public Builder setEllipsizedWidth(@IntRange(from = 0) int ellipsizedWidth) {
-            mEllipsizedWidth = Preconditions.checkArgumentNonnegative(ellipsizedWidth,
-                    "EllipsizedWidth can't be negative");
-            return this;
-        }
-
-        /**
-         * Set maximum number of lines. This is particularly useful in the case of
-         * ellipsizing, where it changes the layout of the last line. The default is
-         * unlimited.
-         *
-         * @param maxLines maximum number of lines in the layout
-         * @return this builder, useful for chaining
-         * @see android.widget.TextView#setMaxLines
-         */
-        @NonNull
-        public Builder setMaxLines(@IntRange(from = 0) int maxLines) {
-            mMaxLines = Preconditions.checkArgumentNonnegative(maxLines,
-                    "MaxLines can't be negative");
-            return this;
-        }
-
-        /**
-         * Set break strategy, useful for selecting high quality or balanced paragraph
-         * layout options. The default is {@link Layout#BREAK_STRATEGY_SIMPLE}.
-         * <p/>
-         * Enabling hyphenation with either using {@link Layout#HYPHENATION_FREQUENCY_NORMAL} or
-         * {@link Layout#HYPHENATION_FREQUENCY_FULL} while line breaking is set to one of
-         * {@link Layout#BREAK_STRATEGY_BALANCED}, {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}
-         * improves the structure of text layout however has performance impact and requires more
-         * time to do the text layout.
-         *
-         * @param breakStrategy break strategy for paragraph layout
-         * @return this builder, useful for chaining
-         * @see android.widget.TextView#setBreakStrategy
-         * @see #setHyphenationFrequency(int)
-         */
-        @NonNull
-        public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
-            mBreakStrategy = breakStrategy;
-            return this;
-        }
-
-        /**
-         * Set hyphenation frequency, to control the amount of automatic hyphenation used. The
-         * possible values are defined in {@link Layout}, by constants named with the pattern
-         * {@code HYPHENATION_FREQUENCY_*}. The default is
-         * {@link Layout#HYPHENATION_FREQUENCY_NONE}.
-         * <p/>
-         * Enabling hyphenation with either using {@link Layout#HYPHENATION_FREQUENCY_NORMAL} or
-         * {@link Layout#HYPHENATION_FREQUENCY_FULL} while line breaking is set to one of
-         * {@link Layout#BREAK_STRATEGY_BALANCED}, {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}
-         * improves the structure of text layout however has performance impact and requires more
-         * time to do the text layout.
-         *
-         * @param hyphenationFrequency hyphenation frequency for the paragraph
-         * @return this builder, useful for chaining
-         * @see android.widget.TextView#setHyphenationFrequency
-         * @see #setBreakStrategy(int)
-         */
-        @NonNull
-        public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) {
-            mHyphenationFrequency = hyphenationFrequency;
-            return this;
-        }
-
-        /**
-         * Set indents. Arguments are arrays holding an indent amount, one per line, measured in
-         * pixels. For lines past the last element in the array, the last element repeats.
-         *
-         * @param leftIndents  array of indent values for left margin, in pixels
-         * @param rightIndents array of indent values for right margin, in pixels
-         * @return this builder, useful for chaining
-         */
-        @NonNull
-        public Builder setIndents(@Nullable int[] leftIndents, @Nullable int[] rightIndents) {
-            mLeftIndents = leftIndents;
-            mRightIndents = rightIndents;
-            return this;
-        }
-
-        /**
-         * Set paragraph justification mode. The default value is
-         * {@link Layout#JUSTIFICATION_MODE_NONE}. If the last line is too short for justification,
-         * the last line will be displayed with the alignment set by {@link #setAlignment}.
-         *
-         * @param justificationMode justification mode for the paragraph.
-         * @return this builder, useful for chaining.
-         */
-        @NonNull
-        public Builder setJustificationMode(@JustificationMode int justificationMode) {
-            mJustificationMode = justificationMode;
-            return this;
-        }
-
-        /**
-         * Sets whether the line spacing should be applied for the last line. Default value is
-         * {@code false}.
-         */
-        @NonNull
-        public Builder setAddLastLineLineSpacing(boolean value) {
-            mAddLastLineLineSpacing = value;
-            return this;
-        }
-
-        /**
-         * Build the {@link StaticLayout} after options have been set.
-         *
-         * @return the newly constructed {@link StaticLayout} object
-         */
-        @NonNull
-        public StaticLayout build() {
-            Preconditions.checkArgumentInRange(mEnd, 0, mText.length(), "End");
-            Preconditions.checkArgumentInRange(mStart, 0, mEnd, "Start");
-
-            if (Build.VERSION.SDK_INT >= 23) {
-                return createWithBuilder();
-            }
-            StaticLayout layout = createWithReflection();
-            if (layout != null) return layout;
-            return createWithConstructor();
-        }
-
-        @RequiresApi(23)
-        @NonNull
-        private StaticLayout createWithBuilder() {
-            StaticLayout.Builder builder = StaticLayout.Builder.obtain(
-                    mText,
-                    mStart,
-                    mEnd,
-                    mPaint,
-                    mWidth)
-                    .setAlignment(mAlignment)
-                    .setTextDirection(mTextDir)
-                    .setLineSpacing(mLineSpacingExtra, mLineSpacingMultiplier)
-                    .setIncludePad(mIncludePadding)
-                    .setEllipsize(mEllipsize)
-                    .setEllipsizedWidth(mEllipsizedWidth)
-                    .setMaxLines(mMaxLines)
-                    .setBreakStrategy(mBreakStrategy)
-                    .setHyphenationFrequency(mHyphenationFrequency)
-                    .setIndents(mLeftIndents, mRightIndents);
-
-            if (Build.VERSION.SDK_INT >= 26) {
-                builder.setJustificationMode(mJustificationMode);
-            }
-            return builder.build();
-        }
-
-        @Nullable
-        private StaticLayout createWithReflection() {
-            obtainStaticLayoutConstructor();
-            if (sStaticLayoutConstructor != null) {
-                try {
-                    return (StaticLayout) sStaticLayoutConstructor.newInstance(
-                            mText,
-                            mStart,
-                            mEnd,
-                            mPaint,
-                            mWidth,
-                            mAlignment,
-                            mTextDir,
-                            mLineSpacingMultiplier,
-                            mLineSpacingExtra,
-                            mIncludePadding,
-                            mEllipsize,
-                            mEllipsizedWidth,
-                            mMaxLines);
-
-                } catch (IllegalAccessException
-                        | InstantiationException
-                        | InvocationTargetException e) {
-                    sStaticLayoutConstructor = null;
-                    Log.e(TAG, "unable to call constructor");
-                }
-            }
-            return null;
-        }
-
-        @SuppressWarnings("deprecation")
-        @NonNull
-        private StaticLayout createWithConstructor() {
-            return new StaticLayout(
-                    mText,
-                    mStart,
-                    mEnd,
-                    mPaint,
-                    mWidth,
-                    mAlignment,
-                    mLineSpacingMultiplier,
-                    mLineSpacingExtra,
-                    mIncludePadding,
-                    mEllipsize,
-                    mEllipsizedWidth);
-        }
-    }
-}
diff --git a/ui/ui-android-text/src/main/java/androidx/text/StaticLayoutFactory.kt b/ui/ui-android-text/src/main/java/androidx/text/StaticLayoutFactory.kt
new file mode 100644
index 0000000..8c767e2
--- /dev/null
+++ b/ui/ui-android-text/src/main/java/androidx/text/StaticLayoutFactory.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2018 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.text
+
+import android.os.Build
+import android.text.Layout.Alignment
+import android.text.StaticLayout
+import android.text.StaticLayout.Builder
+import android.text.TextDirectionHeuristic
+import android.text.TextPaint
+import android.text.TextUtils.TruncateAt
+import android.util.Log
+import androidx.annotation.FloatRange
+import androidx.annotation.IntRange
+import androidx.text.LayoutCompat.BreakStrategy
+import androidx.text.LayoutCompat.HyphenationFrequency
+import androidx.text.LayoutCompat.JustificationMode
+import java.lang.reflect.Constructor
+import java.lang.reflect.InvocationTargetException
+
+internal object StaticLayoutFactory {
+    private const val TAG = "StaticLayoutFactory"
+    private var isInitialized = false
+    private var staticLayoutConstructor: Constructor<StaticLayout>? = null
+
+    private fun obtainStaticLayoutConstructor() {
+        if (isInitialized) return
+        isInitialized = true
+        try {
+            staticLayoutConstructor =
+                StaticLayout::class.java.getConstructor(
+                    CharSequence::class.java,
+                    Int::class.javaPrimitiveType, /* start */
+                    Int::class.javaPrimitiveType, /* end */
+                    TextPaint::class.java,
+                    Int::class.javaPrimitiveType, /* width */
+                    Alignment::class.java,
+                    TextDirectionHeuristic::class.java,
+                    Float::class.javaPrimitiveType, /* lineSpacingMultiplier */
+                    Float::class.javaPrimitiveType, /* lineSpacingExtra */
+                    Boolean::class.javaPrimitiveType, /* includePadding */
+                    TruncateAt::class.java,
+                    Int::class.javaPrimitiveType, /* ellipsizeWidth */
+                    Int::class.javaPrimitiveType /* maxLines */
+                )
+        } catch (e: NoSuchMethodException) {
+            staticLayoutConstructor = null
+            Log.e(TAG, "unable to collect necessary constructor.")
+        }
+    }
+
+    /**
+     * Builder class for StaticLayout.
+     *
+     * @param text The text to be laid out, optionally with spans
+     * @param paint The base paint used for layout
+     * @param width The width in pixels
+     */
+    fun create(
+        text: CharSequence,
+        start: Int = 0,
+        end: Int = text.length,
+        paint: TextPaint,
+        width: Int,
+        textDir: TextDirectionHeuristic = LayoutCompat.DEFAULT_TEXT_DIRECTION_HEURISTIC,
+        alignment: Alignment = LayoutCompat.DEFAULT_LAYOUT_ALIGNMENT,
+        @IntRange(from = 0)
+        maxLines: Int = LayoutCompat.DEFAULT_MAX_LINES,
+        ellipsize: TruncateAt? = null,
+        @IntRange(from = 0)
+        ellipsizedWidth: Int = width,
+        @FloatRange(from = 0.0)
+        lineSpacingMultiplier: Float = LayoutCompat.DEFAULT_LINESPACING_MULTIPLIER,
+        lineSpacingExtra: Float = LayoutCompat.DEFAULT_LINESPACING_EXTRA,
+        @JustificationMode
+        justificationMode: Int = LayoutCompat.DEFAULT_JUSTIFICATION_MODE,
+        includePadding: Boolean = LayoutCompat.DEFAULT_INCLUDE_PADDING,
+        fallbackLineSpacing: Boolean = LayoutCompat.DEFAULT_FALLBACK_LINE_SPACING,
+        @BreakStrategy
+        breakStrategy: Int = LayoutCompat.DEFAULT_BREAK_STRATEGY,
+        @HyphenationFrequency
+        hyphenationFrequency: Int = LayoutCompat.DEFAULT_HYPHENATION_FREQUENCY,
+        leftIndents: IntArray? = null,
+        rightIndents: IntArray? = null
+    ): StaticLayout {
+        require(start in 0..end)
+        require(end in 0..text.length)
+        require(maxLines >= 0)
+        require(width >= 0)
+        require(ellipsizedWidth >= 0)
+        require(lineSpacingMultiplier >= 0f)
+
+        return if (Build.VERSION.SDK_INT >= 23) {
+            Builder.obtain(text, start, end, paint, width)
+                .apply {
+                    setTextDirection(textDir)
+                    setAlignment(alignment)
+                    setMaxLines(maxLines)
+                    setEllipsize(ellipsize)
+                    setEllipsizedWidth(ellipsizedWidth)
+                    setLineSpacing(lineSpacingExtra, lineSpacingMultiplier)
+                    if (Build.VERSION.SDK_INT >= 26) {
+                        setJustificationMode(justificationMode)
+                    }
+                    setIncludePad(includePadding)
+                    if (Build.VERSION.SDK_INT >= 28) {
+                        setUseLineSpacingFromFallbacks(fallbackLineSpacing)
+                    }
+                    setBreakStrategy(breakStrategy)
+                    setHyphenationFrequency(hyphenationFrequency)
+                    setIndents(leftIndents, rightIndents)
+                }.build()
+        } else {
+            // On API 21 to 23, try to call the StaticLayoutConstructor which supports the
+            // textDir and maxLines.
+            obtainStaticLayoutConstructor()
+            staticLayoutConstructor?.let {
+                try {
+                    it.newInstance(
+                        text,
+                        start,
+                        end,
+                        paint,
+                        width,
+                        alignment,
+                        textDir,
+                        lineSpacingMultiplier,
+                        lineSpacingExtra,
+                        includePadding,
+                        ellipsize,
+                        ellipsizedWidth,
+                        maxLines
+                    )
+                } catch (e: IllegalAccessException) {
+                    staticLayoutConstructor = null
+                    Log.e(TAG, "unable to call constructor")
+                    null
+                } catch (e: InstantiationException) {
+                    staticLayoutConstructor = null
+                    Log.e(TAG, "unable to call constructor")
+                    null
+                } catch (e: InvocationTargetException) {
+                    staticLayoutConstructor = null
+                    Log.e(TAG, "unable to call constructor")
+                    null
+                }
+            }
+            // On API 21 to 23 where it failed to find StaticLayout.Builder, create with
+            // deprecated constructor, textDir and maxLines won't work in this case.
+                ?: @Suppress("DEPRECATION") StaticLayout(
+                text,
+                start,
+                end,
+                paint,
+                width,
+                alignment,
+                lineSpacingMultiplier,
+                lineSpacingExtra,
+                includePadding,
+                ellipsize,
+                ellipsizedWidth
+            )
+        }
+    }
+}
diff --git a/ui/ui-android-text/src/main/java/androidx/text/TextLayout.kt b/ui/ui-android-text/src/main/java/androidx/text/TextLayout.kt
index f862707..0009f95 100644
--- a/ui/ui-android-text/src/main/java/androidx/text/TextLayout.kt
+++ b/ui/ui-android-text/src/main/java/androidx/text/TextLayout.kt
@@ -81,7 +81,7 @@
  * element in the array is applied to the corresponding line. For lines past the last element in
  * array, the last element repeats.
  * @param layoutIntrinsics previously calculated [LayoutIntrinsics] for this text
- * @see StaticLayoutCompat
+ * @see StaticLayoutFactory
  *
  * @hide
  */
@@ -152,24 +152,26 @@
                 ellipsizedWidth = widthInt
             )
         } else {
-            StaticLayoutCompat.Builder(
-                charSequence,
-                textPaint,
-                widthInt
+            StaticLayoutFactory.create(
+                text = charSequence,
+                start = 0,
+                end = charSequence.length,
+                paint = textPaint,
+                width = ceil(width).toInt(),
+                textDir = frameworkTextDir,
+                alignment = frameworkAlignment,
+                maxLines = maxLines,
+                ellipsize = ellipsize,
+                ellipsizedWidth = ceil(width).toInt(),
+                lineSpacingMultiplier = lineSpacingMultiplier,
+                lineSpacingExtra = lineSpacingExtra,
+                justificationMode = justificationMode,
+                includePadding = includePadding,
+                breakStrategy = breakStrategy,
+                hyphenationFrequency = hyphenationFrequency,
+                leftIndents = leftIndents,
+                rightIndents = rightIndents
             )
-                .setAlignment(frameworkAlignment)
-                .setTextDirection(frameworkTextDir)
-                .setLineSpacingExtra(lineSpacingExtra)
-                .setLineSpacingMultiplier(lineSpacingMultiplier)
-                .setIncludePad(includePadding)
-                .setEllipsize(ellipsize)
-                .setEllipsizedWidth(widthInt)
-                .setMaxLines(maxLines)
-                .setBreakStrategy(breakStrategy)
-                .setHyphenationFrequency(hyphenationFrequency)
-                .setJustificationMode(justificationMode)
-                .setIndents(leftIndents, rightIndents)
-                .build()
         }
 
         didExceedMaxLines = if (Build.VERSION.SDK_INT <= 25) {
diff --git a/ui/ui-core/api/0.1.0-dev03.txt b/ui/ui-core/api/0.1.0-dev03.txt
index 20c581b..d75234b 100644
--- a/ui/ui-core/api/0.1.0-dev03.txt
+++ b/ui/ui-core/api/0.1.0-dev03.txt
@@ -172,22 +172,22 @@
 
   public interface DensityScope {
     method public androidx.ui.core.Density getDensity();
-    method public default androidx.ui.core.Dp toDp(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Dp toDp(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.Px);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.IntPx);
     method public default androidx.ui.core.Dp toDp(float);
     method public default androidx.ui.core.Dp toDp(int);
     method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Px toPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Px toPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Px toPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.PxSize toPx-s2Mz6-8(long);
     method public default androidx.ui.engine.geometry.Rect toRect(androidx.ui.core.Bounds);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Px);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.IntPx);
-    method public default androidx.ui.core.Sp toSp(float);
-    method public default androidx.ui.core.Sp toSp(int);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Dp);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Px);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.IntPx);
+    method public default androidx.ui.core.TextUnit toSp(float);
+    method public default androidx.ui.core.TextUnit toSp(int);
     property public abstract androidx.ui.core.Density density;
   }
 
@@ -373,39 +373,6 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
-  public final class Em {
-    ctor public Em(float value);
-    method public inline operator int compareTo(androidx.ui.core.Em other);
-    method public float component1();
-    method public androidx.ui.core.Em copy(float value);
-    method public inline operator androidx.ui.core.Em div(float other);
-    method public inline operator androidx.ui.core.Em div(int other);
-    method public inline operator float div(androidx.ui.core.Em other);
-    method public float getValue();
-    method public inline operator androidx.ui.core.Em minus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em plus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em times(float other);
-    method public inline operator androidx.ui.core.Em times(int other);
-    method public inline operator androidx.ui.core.Em unaryMinus();
-  }
-
-  public final class EmKt {
-    ctor public EmKt();
-    method public static inline androidx.ui.core.Em coerceAtLeast(androidx.ui.core.Em, androidx.ui.core.Em minimumValue);
-    method public static inline androidx.ui.core.Em coerceAtMost(androidx.ui.core.Em, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em coerceIn(androidx.ui.core.Em, androidx.ui.core.Em minimumValue, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em getEm(int);
-    method public static inline androidx.ui.core.Em getEm(double);
-    method public static inline androidx.ui.core.Em getEm(float);
-    method public static androidx.ui.core.Em lerp(androidx.ui.core.Em start, androidx.ui.core.Em stop, float fraction);
-    method public static inline androidx.ui.core.Em max(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline androidx.ui.core.Em min(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline operator androidx.ui.core.Em times(float, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(double, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(int, androidx.ui.core.Em other);
-    method public static inline androidx.ui.core.Em toEm(androidx.ui.core.Sp, androidx.ui.core.Sp fontSize);
-  }
-
   public final class HorizontalAlignmentLine extends androidx.ui.core.AlignmentLine {
     ctor public HorizontalAlignmentLine(kotlin.jvm.functions.Function2<? super androidx.ui.core.IntPx,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> merger);
   }
@@ -885,45 +852,69 @@
     method public static String toString-impl(long p);
   }
 
-  public final class Sp {
-    ctor public Sp(float value);
-    method public inline operator int compareTo(androidx.ui.core.Sp other);
-    method public float component1();
-    method public androidx.ui.core.Sp copy(float value);
-    method public inline operator androidx.ui.core.Sp div(float other);
-    method public inline operator androidx.ui.core.Sp div(int other);
-    method public inline operator float div(androidx.ui.core.Sp other);
+  public final class TextUnit {
+    ctor public TextUnit(long packedValue);
+    method public inline operator int compareTo(androidx.ui.core.TextUnit other);
+    method public long component1();
+    method public androidx.ui.core.TextUnit copy(long packedValue);
+    method public inline operator androidx.ui.core.TextUnit div(float other);
+    method public inline operator androidx.ui.core.TextUnit div(double other);
+    method public inline operator androidx.ui.core.TextUnit div(int other);
+    method public inline operator float div(androidx.ui.core.TextUnit other);
+    method public long getPackedValue();
+    method public androidx.ui.core.TextUnitType getType();
     method public float getValue();
-    method public inline operator androidx.ui.core.Sp minus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp plus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp times(float other);
-    method public inline operator androidx.ui.core.Sp times(int other);
-    method public inline operator androidx.ui.core.Sp unaryMinus();
-    field public static final androidx.ui.core.Sp.Companion! Companion;
+    method public boolean isEm();
+    method public boolean isInherit();
+    method public boolean isSp();
+    method public inline operator androidx.ui.core.TextUnit minus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit plus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit times(float other);
+    method public inline operator androidx.ui.core.TextUnit times(double other);
+    method public inline operator androidx.ui.core.TextUnit times(int other);
+    method public inline operator androidx.ui.core.TextUnit unaryMinus();
+    property public final boolean isEm;
+    property public final boolean isInherit;
+    property public final boolean isSp;
+    property public final androidx.ui.core.TextUnitType type;
+    property public final float value;
+    field public static final androidx.ui.core.TextUnit.Companion! Companion;
   }
 
-  public static final class Sp.Companion {
-    method public androidx.ui.core.Sp getInfinity();
-    method public androidx.ui.core.Sp getInherit();
-    property public final androidx.ui.core.Sp Infinity;
-    property public final androidx.ui.core.Sp Inherit;
+  public static final class TextUnit.Companion {
+    method public androidx.ui.core.TextUnit Em(int value);
+    method public androidx.ui.core.TextUnit Em(float value);
+    method public androidx.ui.core.TextUnit Em(double value);
+    method public androidx.ui.core.TextUnit Sp(int value);
+    method public androidx.ui.core.TextUnit Sp(float value);
+    method public androidx.ui.core.TextUnit Sp(double value);
+    method public androidx.ui.core.TextUnit getInherit();
+    property public final androidx.ui.core.TextUnit Inherit;
   }
 
-  public final class SpKt {
-    ctor public SpKt();
-    method public static inline androidx.ui.core.Sp coerceAtLeast(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue);
-    method public static inline androidx.ui.core.Sp coerceAtMost(androidx.ui.core.Sp, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp coerceIn(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp getSp(int);
-    method public static inline androidx.ui.core.Sp getSp(double);
-    method public static inline androidx.ui.core.Sp getSp(float);
-    method public static inline boolean isInherit(androidx.ui.core.Sp);
-    method public static androidx.ui.core.Sp lerp(androidx.ui.core.Sp start, androidx.ui.core.Sp stop, float fraction);
-    method public static inline androidx.ui.core.Sp max(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline androidx.ui.core.Sp min(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline operator androidx.ui.core.Sp times(float, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(double, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(int, androidx.ui.core.Sp other);
+  public final class TextUnitKt {
+    ctor public TextUnitKt();
+    method public static inline androidx.ui.core.TextUnit coerceAtLeast(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue);
+    method public static inline androidx.ui.core.TextUnit coerceAtMost(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit maximumValue);
+    method public static inline androidx.ui.core.TextUnit coerceIn(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue, androidx.ui.core.TextUnit maximumValue);
+    method public static androidx.ui.core.TextUnit getEm(float);
+    method public static androidx.ui.core.TextUnit getEm(double);
+    method public static androidx.ui.core.TextUnit getEm(int);
+    method public static androidx.ui.core.TextUnit getSp(float);
+    method public static androidx.ui.core.TextUnit getSp(double);
+    method public static androidx.ui.core.TextUnit getSp(int);
+    method public static androidx.ui.core.TextUnit lerpTextUnit(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b, float t);
+    method public static inline androidx.ui.core.TextUnit max(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline androidx.ui.core.TextUnit min(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline operator androidx.ui.core.TextUnit times(float, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(double, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(int, androidx.ui.core.TextUnit other);
+  }
+
+  public enum TextUnitType {
+    enum_constant public static final androidx.ui.core.TextUnitType Em;
+    enum_constant public static final androidx.ui.core.TextUnitType Inherit;
+    enum_constant public static final androidx.ui.core.TextUnitType Sp;
   }
 
   public final class Timestamp implements java.lang.Comparable<androidx.ui.core.Timestamp> {
diff --git a/ui/ui-core/api/current.txt b/ui/ui-core/api/current.txt
index 20c581b..d75234b 100644
--- a/ui/ui-core/api/current.txt
+++ b/ui/ui-core/api/current.txt
@@ -172,22 +172,22 @@
 
   public interface DensityScope {
     method public androidx.ui.core.Density getDensity();
-    method public default androidx.ui.core.Dp toDp(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Dp toDp(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.Px);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.IntPx);
     method public default androidx.ui.core.Dp toDp(float);
     method public default androidx.ui.core.Dp toDp(int);
     method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Px toPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Px toPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Px toPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.PxSize toPx-s2Mz6-8(long);
     method public default androidx.ui.engine.geometry.Rect toRect(androidx.ui.core.Bounds);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Px);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.IntPx);
-    method public default androidx.ui.core.Sp toSp(float);
-    method public default androidx.ui.core.Sp toSp(int);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Dp);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Px);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.IntPx);
+    method public default androidx.ui.core.TextUnit toSp(float);
+    method public default androidx.ui.core.TextUnit toSp(int);
     property public abstract androidx.ui.core.Density density;
   }
 
@@ -373,39 +373,6 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
-  public final class Em {
-    ctor public Em(float value);
-    method public inline operator int compareTo(androidx.ui.core.Em other);
-    method public float component1();
-    method public androidx.ui.core.Em copy(float value);
-    method public inline operator androidx.ui.core.Em div(float other);
-    method public inline operator androidx.ui.core.Em div(int other);
-    method public inline operator float div(androidx.ui.core.Em other);
-    method public float getValue();
-    method public inline operator androidx.ui.core.Em minus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em plus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em times(float other);
-    method public inline operator androidx.ui.core.Em times(int other);
-    method public inline operator androidx.ui.core.Em unaryMinus();
-  }
-
-  public final class EmKt {
-    ctor public EmKt();
-    method public static inline androidx.ui.core.Em coerceAtLeast(androidx.ui.core.Em, androidx.ui.core.Em minimumValue);
-    method public static inline androidx.ui.core.Em coerceAtMost(androidx.ui.core.Em, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em coerceIn(androidx.ui.core.Em, androidx.ui.core.Em minimumValue, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em getEm(int);
-    method public static inline androidx.ui.core.Em getEm(double);
-    method public static inline androidx.ui.core.Em getEm(float);
-    method public static androidx.ui.core.Em lerp(androidx.ui.core.Em start, androidx.ui.core.Em stop, float fraction);
-    method public static inline androidx.ui.core.Em max(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline androidx.ui.core.Em min(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline operator androidx.ui.core.Em times(float, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(double, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(int, androidx.ui.core.Em other);
-    method public static inline androidx.ui.core.Em toEm(androidx.ui.core.Sp, androidx.ui.core.Sp fontSize);
-  }
-
   public final class HorizontalAlignmentLine extends androidx.ui.core.AlignmentLine {
     ctor public HorizontalAlignmentLine(kotlin.jvm.functions.Function2<? super androidx.ui.core.IntPx,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> merger);
   }
@@ -885,45 +852,69 @@
     method public static String toString-impl(long p);
   }
 
-  public final class Sp {
-    ctor public Sp(float value);
-    method public inline operator int compareTo(androidx.ui.core.Sp other);
-    method public float component1();
-    method public androidx.ui.core.Sp copy(float value);
-    method public inline operator androidx.ui.core.Sp div(float other);
-    method public inline operator androidx.ui.core.Sp div(int other);
-    method public inline operator float div(androidx.ui.core.Sp other);
+  public final class TextUnit {
+    ctor public TextUnit(long packedValue);
+    method public inline operator int compareTo(androidx.ui.core.TextUnit other);
+    method public long component1();
+    method public androidx.ui.core.TextUnit copy(long packedValue);
+    method public inline operator androidx.ui.core.TextUnit div(float other);
+    method public inline operator androidx.ui.core.TextUnit div(double other);
+    method public inline operator androidx.ui.core.TextUnit div(int other);
+    method public inline operator float div(androidx.ui.core.TextUnit other);
+    method public long getPackedValue();
+    method public androidx.ui.core.TextUnitType getType();
     method public float getValue();
-    method public inline operator androidx.ui.core.Sp minus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp plus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp times(float other);
-    method public inline operator androidx.ui.core.Sp times(int other);
-    method public inline operator androidx.ui.core.Sp unaryMinus();
-    field public static final androidx.ui.core.Sp.Companion! Companion;
+    method public boolean isEm();
+    method public boolean isInherit();
+    method public boolean isSp();
+    method public inline operator androidx.ui.core.TextUnit minus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit plus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit times(float other);
+    method public inline operator androidx.ui.core.TextUnit times(double other);
+    method public inline operator androidx.ui.core.TextUnit times(int other);
+    method public inline operator androidx.ui.core.TextUnit unaryMinus();
+    property public final boolean isEm;
+    property public final boolean isInherit;
+    property public final boolean isSp;
+    property public final androidx.ui.core.TextUnitType type;
+    property public final float value;
+    field public static final androidx.ui.core.TextUnit.Companion! Companion;
   }
 
-  public static final class Sp.Companion {
-    method public androidx.ui.core.Sp getInfinity();
-    method public androidx.ui.core.Sp getInherit();
-    property public final androidx.ui.core.Sp Infinity;
-    property public final androidx.ui.core.Sp Inherit;
+  public static final class TextUnit.Companion {
+    method public androidx.ui.core.TextUnit Em(int value);
+    method public androidx.ui.core.TextUnit Em(float value);
+    method public androidx.ui.core.TextUnit Em(double value);
+    method public androidx.ui.core.TextUnit Sp(int value);
+    method public androidx.ui.core.TextUnit Sp(float value);
+    method public androidx.ui.core.TextUnit Sp(double value);
+    method public androidx.ui.core.TextUnit getInherit();
+    property public final androidx.ui.core.TextUnit Inherit;
   }
 
-  public final class SpKt {
-    ctor public SpKt();
-    method public static inline androidx.ui.core.Sp coerceAtLeast(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue);
-    method public static inline androidx.ui.core.Sp coerceAtMost(androidx.ui.core.Sp, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp coerceIn(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp getSp(int);
-    method public static inline androidx.ui.core.Sp getSp(double);
-    method public static inline androidx.ui.core.Sp getSp(float);
-    method public static inline boolean isInherit(androidx.ui.core.Sp);
-    method public static androidx.ui.core.Sp lerp(androidx.ui.core.Sp start, androidx.ui.core.Sp stop, float fraction);
-    method public static inline androidx.ui.core.Sp max(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline androidx.ui.core.Sp min(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline operator androidx.ui.core.Sp times(float, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(double, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(int, androidx.ui.core.Sp other);
+  public final class TextUnitKt {
+    ctor public TextUnitKt();
+    method public static inline androidx.ui.core.TextUnit coerceAtLeast(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue);
+    method public static inline androidx.ui.core.TextUnit coerceAtMost(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit maximumValue);
+    method public static inline androidx.ui.core.TextUnit coerceIn(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue, androidx.ui.core.TextUnit maximumValue);
+    method public static androidx.ui.core.TextUnit getEm(float);
+    method public static androidx.ui.core.TextUnit getEm(double);
+    method public static androidx.ui.core.TextUnit getEm(int);
+    method public static androidx.ui.core.TextUnit getSp(float);
+    method public static androidx.ui.core.TextUnit getSp(double);
+    method public static androidx.ui.core.TextUnit getSp(int);
+    method public static androidx.ui.core.TextUnit lerpTextUnit(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b, float t);
+    method public static inline androidx.ui.core.TextUnit max(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline androidx.ui.core.TextUnit min(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline operator androidx.ui.core.TextUnit times(float, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(double, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(int, androidx.ui.core.TextUnit other);
+  }
+
+  public enum TextUnitType {
+    enum_constant public static final androidx.ui.core.TextUnitType Em;
+    enum_constant public static final androidx.ui.core.TextUnitType Inherit;
+    enum_constant public static final androidx.ui.core.TextUnitType Sp;
   }
 
   public final class Timestamp implements java.lang.Comparable<androidx.ui.core.Timestamp> {
diff --git a/ui/ui-core/api/public_plus_experimental_0.1.0-dev03.txt b/ui/ui-core/api/public_plus_experimental_0.1.0-dev03.txt
index 20c581b..d75234b 100644
--- a/ui/ui-core/api/public_plus_experimental_0.1.0-dev03.txt
+++ b/ui/ui-core/api/public_plus_experimental_0.1.0-dev03.txt
@@ -172,22 +172,22 @@
 
   public interface DensityScope {
     method public androidx.ui.core.Density getDensity();
-    method public default androidx.ui.core.Dp toDp(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Dp toDp(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.Px);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.IntPx);
     method public default androidx.ui.core.Dp toDp(float);
     method public default androidx.ui.core.Dp toDp(int);
     method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Px toPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Px toPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Px toPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.PxSize toPx-s2Mz6-8(long);
     method public default androidx.ui.engine.geometry.Rect toRect(androidx.ui.core.Bounds);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Px);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.IntPx);
-    method public default androidx.ui.core.Sp toSp(float);
-    method public default androidx.ui.core.Sp toSp(int);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Dp);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Px);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.IntPx);
+    method public default androidx.ui.core.TextUnit toSp(float);
+    method public default androidx.ui.core.TextUnit toSp(int);
     property public abstract androidx.ui.core.Density density;
   }
 
@@ -373,39 +373,6 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
-  public final class Em {
-    ctor public Em(float value);
-    method public inline operator int compareTo(androidx.ui.core.Em other);
-    method public float component1();
-    method public androidx.ui.core.Em copy(float value);
-    method public inline operator androidx.ui.core.Em div(float other);
-    method public inline operator androidx.ui.core.Em div(int other);
-    method public inline operator float div(androidx.ui.core.Em other);
-    method public float getValue();
-    method public inline operator androidx.ui.core.Em minus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em plus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em times(float other);
-    method public inline operator androidx.ui.core.Em times(int other);
-    method public inline operator androidx.ui.core.Em unaryMinus();
-  }
-
-  public final class EmKt {
-    ctor public EmKt();
-    method public static inline androidx.ui.core.Em coerceAtLeast(androidx.ui.core.Em, androidx.ui.core.Em minimumValue);
-    method public static inline androidx.ui.core.Em coerceAtMost(androidx.ui.core.Em, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em coerceIn(androidx.ui.core.Em, androidx.ui.core.Em minimumValue, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em getEm(int);
-    method public static inline androidx.ui.core.Em getEm(double);
-    method public static inline androidx.ui.core.Em getEm(float);
-    method public static androidx.ui.core.Em lerp(androidx.ui.core.Em start, androidx.ui.core.Em stop, float fraction);
-    method public static inline androidx.ui.core.Em max(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline androidx.ui.core.Em min(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline operator androidx.ui.core.Em times(float, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(double, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(int, androidx.ui.core.Em other);
-    method public static inline androidx.ui.core.Em toEm(androidx.ui.core.Sp, androidx.ui.core.Sp fontSize);
-  }
-
   public final class HorizontalAlignmentLine extends androidx.ui.core.AlignmentLine {
     ctor public HorizontalAlignmentLine(kotlin.jvm.functions.Function2<? super androidx.ui.core.IntPx,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> merger);
   }
@@ -885,45 +852,69 @@
     method public static String toString-impl(long p);
   }
 
-  public final class Sp {
-    ctor public Sp(float value);
-    method public inline operator int compareTo(androidx.ui.core.Sp other);
-    method public float component1();
-    method public androidx.ui.core.Sp copy(float value);
-    method public inline operator androidx.ui.core.Sp div(float other);
-    method public inline operator androidx.ui.core.Sp div(int other);
-    method public inline operator float div(androidx.ui.core.Sp other);
+  public final class TextUnit {
+    ctor public TextUnit(long packedValue);
+    method public inline operator int compareTo(androidx.ui.core.TextUnit other);
+    method public long component1();
+    method public androidx.ui.core.TextUnit copy(long packedValue);
+    method public inline operator androidx.ui.core.TextUnit div(float other);
+    method public inline operator androidx.ui.core.TextUnit div(double other);
+    method public inline operator androidx.ui.core.TextUnit div(int other);
+    method public inline operator float div(androidx.ui.core.TextUnit other);
+    method public long getPackedValue();
+    method public androidx.ui.core.TextUnitType getType();
     method public float getValue();
-    method public inline operator androidx.ui.core.Sp minus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp plus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp times(float other);
-    method public inline operator androidx.ui.core.Sp times(int other);
-    method public inline operator androidx.ui.core.Sp unaryMinus();
-    field public static final androidx.ui.core.Sp.Companion! Companion;
+    method public boolean isEm();
+    method public boolean isInherit();
+    method public boolean isSp();
+    method public inline operator androidx.ui.core.TextUnit minus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit plus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit times(float other);
+    method public inline operator androidx.ui.core.TextUnit times(double other);
+    method public inline operator androidx.ui.core.TextUnit times(int other);
+    method public inline operator androidx.ui.core.TextUnit unaryMinus();
+    property public final boolean isEm;
+    property public final boolean isInherit;
+    property public final boolean isSp;
+    property public final androidx.ui.core.TextUnitType type;
+    property public final float value;
+    field public static final androidx.ui.core.TextUnit.Companion! Companion;
   }
 
-  public static final class Sp.Companion {
-    method public androidx.ui.core.Sp getInfinity();
-    method public androidx.ui.core.Sp getInherit();
-    property public final androidx.ui.core.Sp Infinity;
-    property public final androidx.ui.core.Sp Inherit;
+  public static final class TextUnit.Companion {
+    method public androidx.ui.core.TextUnit Em(int value);
+    method public androidx.ui.core.TextUnit Em(float value);
+    method public androidx.ui.core.TextUnit Em(double value);
+    method public androidx.ui.core.TextUnit Sp(int value);
+    method public androidx.ui.core.TextUnit Sp(float value);
+    method public androidx.ui.core.TextUnit Sp(double value);
+    method public androidx.ui.core.TextUnit getInherit();
+    property public final androidx.ui.core.TextUnit Inherit;
   }
 
-  public final class SpKt {
-    ctor public SpKt();
-    method public static inline androidx.ui.core.Sp coerceAtLeast(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue);
-    method public static inline androidx.ui.core.Sp coerceAtMost(androidx.ui.core.Sp, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp coerceIn(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp getSp(int);
-    method public static inline androidx.ui.core.Sp getSp(double);
-    method public static inline androidx.ui.core.Sp getSp(float);
-    method public static inline boolean isInherit(androidx.ui.core.Sp);
-    method public static androidx.ui.core.Sp lerp(androidx.ui.core.Sp start, androidx.ui.core.Sp stop, float fraction);
-    method public static inline androidx.ui.core.Sp max(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline androidx.ui.core.Sp min(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline operator androidx.ui.core.Sp times(float, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(double, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(int, androidx.ui.core.Sp other);
+  public final class TextUnitKt {
+    ctor public TextUnitKt();
+    method public static inline androidx.ui.core.TextUnit coerceAtLeast(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue);
+    method public static inline androidx.ui.core.TextUnit coerceAtMost(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit maximumValue);
+    method public static inline androidx.ui.core.TextUnit coerceIn(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue, androidx.ui.core.TextUnit maximumValue);
+    method public static androidx.ui.core.TextUnit getEm(float);
+    method public static androidx.ui.core.TextUnit getEm(double);
+    method public static androidx.ui.core.TextUnit getEm(int);
+    method public static androidx.ui.core.TextUnit getSp(float);
+    method public static androidx.ui.core.TextUnit getSp(double);
+    method public static androidx.ui.core.TextUnit getSp(int);
+    method public static androidx.ui.core.TextUnit lerpTextUnit(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b, float t);
+    method public static inline androidx.ui.core.TextUnit max(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline androidx.ui.core.TextUnit min(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline operator androidx.ui.core.TextUnit times(float, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(double, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(int, androidx.ui.core.TextUnit other);
+  }
+
+  public enum TextUnitType {
+    enum_constant public static final androidx.ui.core.TextUnitType Em;
+    enum_constant public static final androidx.ui.core.TextUnitType Inherit;
+    enum_constant public static final androidx.ui.core.TextUnitType Sp;
   }
 
   public final class Timestamp implements java.lang.Comparable<androidx.ui.core.Timestamp> {
diff --git a/ui/ui-core/api/public_plus_experimental_current.txt b/ui/ui-core/api/public_plus_experimental_current.txt
index 20c581b..d75234b 100644
--- a/ui/ui-core/api/public_plus_experimental_current.txt
+++ b/ui/ui-core/api/public_plus_experimental_current.txt
@@ -172,22 +172,22 @@
 
   public interface DensityScope {
     method public androidx.ui.core.Density getDensity();
-    method public default androidx.ui.core.Dp toDp(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Dp toDp(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.Px);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.IntPx);
     method public default androidx.ui.core.Dp toDp(float);
     method public default androidx.ui.core.Dp toDp(int);
     method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Px toPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Px toPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Px toPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.PxSize toPx-s2Mz6-8(long);
     method public default androidx.ui.engine.geometry.Rect toRect(androidx.ui.core.Bounds);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Px);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.IntPx);
-    method public default androidx.ui.core.Sp toSp(float);
-    method public default androidx.ui.core.Sp toSp(int);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Dp);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Px);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.IntPx);
+    method public default androidx.ui.core.TextUnit toSp(float);
+    method public default androidx.ui.core.TextUnit toSp(int);
     property public abstract androidx.ui.core.Density density;
   }
 
@@ -373,39 +373,6 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
-  public final class Em {
-    ctor public Em(float value);
-    method public inline operator int compareTo(androidx.ui.core.Em other);
-    method public float component1();
-    method public androidx.ui.core.Em copy(float value);
-    method public inline operator androidx.ui.core.Em div(float other);
-    method public inline operator androidx.ui.core.Em div(int other);
-    method public inline operator float div(androidx.ui.core.Em other);
-    method public float getValue();
-    method public inline operator androidx.ui.core.Em minus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em plus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em times(float other);
-    method public inline operator androidx.ui.core.Em times(int other);
-    method public inline operator androidx.ui.core.Em unaryMinus();
-  }
-
-  public final class EmKt {
-    ctor public EmKt();
-    method public static inline androidx.ui.core.Em coerceAtLeast(androidx.ui.core.Em, androidx.ui.core.Em minimumValue);
-    method public static inline androidx.ui.core.Em coerceAtMost(androidx.ui.core.Em, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em coerceIn(androidx.ui.core.Em, androidx.ui.core.Em minimumValue, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em getEm(int);
-    method public static inline androidx.ui.core.Em getEm(double);
-    method public static inline androidx.ui.core.Em getEm(float);
-    method public static androidx.ui.core.Em lerp(androidx.ui.core.Em start, androidx.ui.core.Em stop, float fraction);
-    method public static inline androidx.ui.core.Em max(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline androidx.ui.core.Em min(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline operator androidx.ui.core.Em times(float, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(double, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(int, androidx.ui.core.Em other);
-    method public static inline androidx.ui.core.Em toEm(androidx.ui.core.Sp, androidx.ui.core.Sp fontSize);
-  }
-
   public final class HorizontalAlignmentLine extends androidx.ui.core.AlignmentLine {
     ctor public HorizontalAlignmentLine(kotlin.jvm.functions.Function2<? super androidx.ui.core.IntPx,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> merger);
   }
@@ -885,45 +852,69 @@
     method public static String toString-impl(long p);
   }
 
-  public final class Sp {
-    ctor public Sp(float value);
-    method public inline operator int compareTo(androidx.ui.core.Sp other);
-    method public float component1();
-    method public androidx.ui.core.Sp copy(float value);
-    method public inline operator androidx.ui.core.Sp div(float other);
-    method public inline operator androidx.ui.core.Sp div(int other);
-    method public inline operator float div(androidx.ui.core.Sp other);
+  public final class TextUnit {
+    ctor public TextUnit(long packedValue);
+    method public inline operator int compareTo(androidx.ui.core.TextUnit other);
+    method public long component1();
+    method public androidx.ui.core.TextUnit copy(long packedValue);
+    method public inline operator androidx.ui.core.TextUnit div(float other);
+    method public inline operator androidx.ui.core.TextUnit div(double other);
+    method public inline operator androidx.ui.core.TextUnit div(int other);
+    method public inline operator float div(androidx.ui.core.TextUnit other);
+    method public long getPackedValue();
+    method public androidx.ui.core.TextUnitType getType();
     method public float getValue();
-    method public inline operator androidx.ui.core.Sp minus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp plus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp times(float other);
-    method public inline operator androidx.ui.core.Sp times(int other);
-    method public inline operator androidx.ui.core.Sp unaryMinus();
-    field public static final androidx.ui.core.Sp.Companion! Companion;
+    method public boolean isEm();
+    method public boolean isInherit();
+    method public boolean isSp();
+    method public inline operator androidx.ui.core.TextUnit minus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit plus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit times(float other);
+    method public inline operator androidx.ui.core.TextUnit times(double other);
+    method public inline operator androidx.ui.core.TextUnit times(int other);
+    method public inline operator androidx.ui.core.TextUnit unaryMinus();
+    property public final boolean isEm;
+    property public final boolean isInherit;
+    property public final boolean isSp;
+    property public final androidx.ui.core.TextUnitType type;
+    property public final float value;
+    field public static final androidx.ui.core.TextUnit.Companion! Companion;
   }
 
-  public static final class Sp.Companion {
-    method public androidx.ui.core.Sp getInfinity();
-    method public androidx.ui.core.Sp getInherit();
-    property public final androidx.ui.core.Sp Infinity;
-    property public final androidx.ui.core.Sp Inherit;
+  public static final class TextUnit.Companion {
+    method public androidx.ui.core.TextUnit Em(int value);
+    method public androidx.ui.core.TextUnit Em(float value);
+    method public androidx.ui.core.TextUnit Em(double value);
+    method public androidx.ui.core.TextUnit Sp(int value);
+    method public androidx.ui.core.TextUnit Sp(float value);
+    method public androidx.ui.core.TextUnit Sp(double value);
+    method public androidx.ui.core.TextUnit getInherit();
+    property public final androidx.ui.core.TextUnit Inherit;
   }
 
-  public final class SpKt {
-    ctor public SpKt();
-    method public static inline androidx.ui.core.Sp coerceAtLeast(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue);
-    method public static inline androidx.ui.core.Sp coerceAtMost(androidx.ui.core.Sp, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp coerceIn(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp getSp(int);
-    method public static inline androidx.ui.core.Sp getSp(double);
-    method public static inline androidx.ui.core.Sp getSp(float);
-    method public static inline boolean isInherit(androidx.ui.core.Sp);
-    method public static androidx.ui.core.Sp lerp(androidx.ui.core.Sp start, androidx.ui.core.Sp stop, float fraction);
-    method public static inline androidx.ui.core.Sp max(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline androidx.ui.core.Sp min(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline operator androidx.ui.core.Sp times(float, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(double, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(int, androidx.ui.core.Sp other);
+  public final class TextUnitKt {
+    ctor public TextUnitKt();
+    method public static inline androidx.ui.core.TextUnit coerceAtLeast(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue);
+    method public static inline androidx.ui.core.TextUnit coerceAtMost(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit maximumValue);
+    method public static inline androidx.ui.core.TextUnit coerceIn(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue, androidx.ui.core.TextUnit maximumValue);
+    method public static androidx.ui.core.TextUnit getEm(float);
+    method public static androidx.ui.core.TextUnit getEm(double);
+    method public static androidx.ui.core.TextUnit getEm(int);
+    method public static androidx.ui.core.TextUnit getSp(float);
+    method public static androidx.ui.core.TextUnit getSp(double);
+    method public static androidx.ui.core.TextUnit getSp(int);
+    method public static androidx.ui.core.TextUnit lerpTextUnit(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b, float t);
+    method public static inline androidx.ui.core.TextUnit max(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline androidx.ui.core.TextUnit min(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline operator androidx.ui.core.TextUnit times(float, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(double, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(int, androidx.ui.core.TextUnit other);
+  }
+
+  public enum TextUnitType {
+    enum_constant public static final androidx.ui.core.TextUnitType Em;
+    enum_constant public static final androidx.ui.core.TextUnitType Inherit;
+    enum_constant public static final androidx.ui.core.TextUnitType Sp;
   }
 
   public final class Timestamp implements java.lang.Comparable<androidx.ui.core.Timestamp> {
diff --git a/ui/ui-core/api/restricted_0.1.0-dev03.txt b/ui/ui-core/api/restricted_0.1.0-dev03.txt
index 20c581b..d75234b 100644
--- a/ui/ui-core/api/restricted_0.1.0-dev03.txt
+++ b/ui/ui-core/api/restricted_0.1.0-dev03.txt
@@ -172,22 +172,22 @@
 
   public interface DensityScope {
     method public androidx.ui.core.Density getDensity();
-    method public default androidx.ui.core.Dp toDp(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Dp toDp(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.Px);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.IntPx);
     method public default androidx.ui.core.Dp toDp(float);
     method public default androidx.ui.core.Dp toDp(int);
     method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Px toPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Px toPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Px toPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.PxSize toPx-s2Mz6-8(long);
     method public default androidx.ui.engine.geometry.Rect toRect(androidx.ui.core.Bounds);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Px);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.IntPx);
-    method public default androidx.ui.core.Sp toSp(float);
-    method public default androidx.ui.core.Sp toSp(int);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Dp);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Px);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.IntPx);
+    method public default androidx.ui.core.TextUnit toSp(float);
+    method public default androidx.ui.core.TextUnit toSp(int);
     property public abstract androidx.ui.core.Density density;
   }
 
@@ -373,39 +373,6 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
-  public final class Em {
-    ctor public Em(float value);
-    method public inline operator int compareTo(androidx.ui.core.Em other);
-    method public float component1();
-    method public androidx.ui.core.Em copy(float value);
-    method public inline operator androidx.ui.core.Em div(float other);
-    method public inline operator androidx.ui.core.Em div(int other);
-    method public inline operator float div(androidx.ui.core.Em other);
-    method public float getValue();
-    method public inline operator androidx.ui.core.Em minus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em plus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em times(float other);
-    method public inline operator androidx.ui.core.Em times(int other);
-    method public inline operator androidx.ui.core.Em unaryMinus();
-  }
-
-  public final class EmKt {
-    ctor public EmKt();
-    method public static inline androidx.ui.core.Em coerceAtLeast(androidx.ui.core.Em, androidx.ui.core.Em minimumValue);
-    method public static inline androidx.ui.core.Em coerceAtMost(androidx.ui.core.Em, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em coerceIn(androidx.ui.core.Em, androidx.ui.core.Em minimumValue, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em getEm(int);
-    method public static inline androidx.ui.core.Em getEm(double);
-    method public static inline androidx.ui.core.Em getEm(float);
-    method public static androidx.ui.core.Em lerp(androidx.ui.core.Em start, androidx.ui.core.Em stop, float fraction);
-    method public static inline androidx.ui.core.Em max(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline androidx.ui.core.Em min(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline operator androidx.ui.core.Em times(float, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(double, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(int, androidx.ui.core.Em other);
-    method public static inline androidx.ui.core.Em toEm(androidx.ui.core.Sp, androidx.ui.core.Sp fontSize);
-  }
-
   public final class HorizontalAlignmentLine extends androidx.ui.core.AlignmentLine {
     ctor public HorizontalAlignmentLine(kotlin.jvm.functions.Function2<? super androidx.ui.core.IntPx,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> merger);
   }
@@ -885,45 +852,69 @@
     method public static String toString-impl(long p);
   }
 
-  public final class Sp {
-    ctor public Sp(float value);
-    method public inline operator int compareTo(androidx.ui.core.Sp other);
-    method public float component1();
-    method public androidx.ui.core.Sp copy(float value);
-    method public inline operator androidx.ui.core.Sp div(float other);
-    method public inline operator androidx.ui.core.Sp div(int other);
-    method public inline operator float div(androidx.ui.core.Sp other);
+  public final class TextUnit {
+    ctor public TextUnit(long packedValue);
+    method public inline operator int compareTo(androidx.ui.core.TextUnit other);
+    method public long component1();
+    method public androidx.ui.core.TextUnit copy(long packedValue);
+    method public inline operator androidx.ui.core.TextUnit div(float other);
+    method public inline operator androidx.ui.core.TextUnit div(double other);
+    method public inline operator androidx.ui.core.TextUnit div(int other);
+    method public inline operator float div(androidx.ui.core.TextUnit other);
+    method public long getPackedValue();
+    method public androidx.ui.core.TextUnitType getType();
     method public float getValue();
-    method public inline operator androidx.ui.core.Sp minus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp plus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp times(float other);
-    method public inline operator androidx.ui.core.Sp times(int other);
-    method public inline operator androidx.ui.core.Sp unaryMinus();
-    field public static final androidx.ui.core.Sp.Companion! Companion;
+    method public boolean isEm();
+    method public boolean isInherit();
+    method public boolean isSp();
+    method public inline operator androidx.ui.core.TextUnit minus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit plus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit times(float other);
+    method public inline operator androidx.ui.core.TextUnit times(double other);
+    method public inline operator androidx.ui.core.TextUnit times(int other);
+    method public inline operator androidx.ui.core.TextUnit unaryMinus();
+    property public final boolean isEm;
+    property public final boolean isInherit;
+    property public final boolean isSp;
+    property public final androidx.ui.core.TextUnitType type;
+    property public final float value;
+    field public static final androidx.ui.core.TextUnit.Companion! Companion;
   }
 
-  public static final class Sp.Companion {
-    method public androidx.ui.core.Sp getInfinity();
-    method public androidx.ui.core.Sp getInherit();
-    property public final androidx.ui.core.Sp Infinity;
-    property public final androidx.ui.core.Sp Inherit;
+  public static final class TextUnit.Companion {
+    method public androidx.ui.core.TextUnit Em(int value);
+    method public androidx.ui.core.TextUnit Em(float value);
+    method public androidx.ui.core.TextUnit Em(double value);
+    method public androidx.ui.core.TextUnit Sp(int value);
+    method public androidx.ui.core.TextUnit Sp(float value);
+    method public androidx.ui.core.TextUnit Sp(double value);
+    method public androidx.ui.core.TextUnit getInherit();
+    property public final androidx.ui.core.TextUnit Inherit;
   }
 
-  public final class SpKt {
-    ctor public SpKt();
-    method public static inline androidx.ui.core.Sp coerceAtLeast(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue);
-    method public static inline androidx.ui.core.Sp coerceAtMost(androidx.ui.core.Sp, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp coerceIn(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp getSp(int);
-    method public static inline androidx.ui.core.Sp getSp(double);
-    method public static inline androidx.ui.core.Sp getSp(float);
-    method public static inline boolean isInherit(androidx.ui.core.Sp);
-    method public static androidx.ui.core.Sp lerp(androidx.ui.core.Sp start, androidx.ui.core.Sp stop, float fraction);
-    method public static inline androidx.ui.core.Sp max(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline androidx.ui.core.Sp min(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline operator androidx.ui.core.Sp times(float, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(double, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(int, androidx.ui.core.Sp other);
+  public final class TextUnitKt {
+    ctor public TextUnitKt();
+    method public static inline androidx.ui.core.TextUnit coerceAtLeast(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue);
+    method public static inline androidx.ui.core.TextUnit coerceAtMost(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit maximumValue);
+    method public static inline androidx.ui.core.TextUnit coerceIn(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue, androidx.ui.core.TextUnit maximumValue);
+    method public static androidx.ui.core.TextUnit getEm(float);
+    method public static androidx.ui.core.TextUnit getEm(double);
+    method public static androidx.ui.core.TextUnit getEm(int);
+    method public static androidx.ui.core.TextUnit getSp(float);
+    method public static androidx.ui.core.TextUnit getSp(double);
+    method public static androidx.ui.core.TextUnit getSp(int);
+    method public static androidx.ui.core.TextUnit lerpTextUnit(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b, float t);
+    method public static inline androidx.ui.core.TextUnit max(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline androidx.ui.core.TextUnit min(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline operator androidx.ui.core.TextUnit times(float, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(double, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(int, androidx.ui.core.TextUnit other);
+  }
+
+  public enum TextUnitType {
+    enum_constant public static final androidx.ui.core.TextUnitType Em;
+    enum_constant public static final androidx.ui.core.TextUnitType Inherit;
+    enum_constant public static final androidx.ui.core.TextUnitType Sp;
   }
 
   public final class Timestamp implements java.lang.Comparable<androidx.ui.core.Timestamp> {
diff --git a/ui/ui-core/api/restricted_current.txt b/ui/ui-core/api/restricted_current.txt
index 20c581b..d75234b 100644
--- a/ui/ui-core/api/restricted_current.txt
+++ b/ui/ui-core/api/restricted_current.txt
@@ -172,22 +172,22 @@
 
   public interface DensityScope {
     method public androidx.ui.core.Density getDensity();
-    method public default androidx.ui.core.Dp toDp(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Dp toDp(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.Px);
     method public default androidx.ui.core.Dp toDp(androidx.ui.core.IntPx);
     method public default androidx.ui.core.Dp toDp(float);
     method public default androidx.ui.core.Dp toDp(int);
     method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.IntPx toIntPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.Px toPx(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Px toPx(androidx.ui.core.Sp);
+    method public default androidx.ui.core.Px toPx(androidx.ui.core.TextUnit);
     method public default androidx.ui.core.PxSize toPx-s2Mz6-8(long);
     method public default androidx.ui.engine.geometry.Rect toRect(androidx.ui.core.Bounds);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Dp);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.Px);
-    method public default androidx.ui.core.Sp toSp(androidx.ui.core.IntPx);
-    method public default androidx.ui.core.Sp toSp(float);
-    method public default androidx.ui.core.Sp toSp(int);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Dp);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.Px);
+    method public default androidx.ui.core.TextUnit toSp(androidx.ui.core.IntPx);
+    method public default androidx.ui.core.TextUnit toSp(float);
+    method public default androidx.ui.core.TextUnit toSp(int);
     property public abstract androidx.ui.core.Density density;
   }
 
@@ -373,39 +373,6 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
-  public final class Em {
-    ctor public Em(float value);
-    method public inline operator int compareTo(androidx.ui.core.Em other);
-    method public float component1();
-    method public androidx.ui.core.Em copy(float value);
-    method public inline operator androidx.ui.core.Em div(float other);
-    method public inline operator androidx.ui.core.Em div(int other);
-    method public inline operator float div(androidx.ui.core.Em other);
-    method public float getValue();
-    method public inline operator androidx.ui.core.Em minus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em plus(androidx.ui.core.Em other);
-    method public inline operator androidx.ui.core.Em times(float other);
-    method public inline operator androidx.ui.core.Em times(int other);
-    method public inline operator androidx.ui.core.Em unaryMinus();
-  }
-
-  public final class EmKt {
-    ctor public EmKt();
-    method public static inline androidx.ui.core.Em coerceAtLeast(androidx.ui.core.Em, androidx.ui.core.Em minimumValue);
-    method public static inline androidx.ui.core.Em coerceAtMost(androidx.ui.core.Em, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em coerceIn(androidx.ui.core.Em, androidx.ui.core.Em minimumValue, androidx.ui.core.Em maximumValue);
-    method public static inline androidx.ui.core.Em getEm(int);
-    method public static inline androidx.ui.core.Em getEm(double);
-    method public static inline androidx.ui.core.Em getEm(float);
-    method public static androidx.ui.core.Em lerp(androidx.ui.core.Em start, androidx.ui.core.Em stop, float fraction);
-    method public static inline androidx.ui.core.Em max(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline androidx.ui.core.Em min(androidx.ui.core.Em a, androidx.ui.core.Em b);
-    method public static inline operator androidx.ui.core.Em times(float, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(double, androidx.ui.core.Em other);
-    method public static inline operator androidx.ui.core.Em times(int, androidx.ui.core.Em other);
-    method public static inline androidx.ui.core.Em toEm(androidx.ui.core.Sp, androidx.ui.core.Sp fontSize);
-  }
-
   public final class HorizontalAlignmentLine extends androidx.ui.core.AlignmentLine {
     ctor public HorizontalAlignmentLine(kotlin.jvm.functions.Function2<? super androidx.ui.core.IntPx,? super androidx.ui.core.IntPx,androidx.ui.core.IntPx> merger);
   }
@@ -885,45 +852,69 @@
     method public static String toString-impl(long p);
   }
 
-  public final class Sp {
-    ctor public Sp(float value);
-    method public inline operator int compareTo(androidx.ui.core.Sp other);
-    method public float component1();
-    method public androidx.ui.core.Sp copy(float value);
-    method public inline operator androidx.ui.core.Sp div(float other);
-    method public inline operator androidx.ui.core.Sp div(int other);
-    method public inline operator float div(androidx.ui.core.Sp other);
+  public final class TextUnit {
+    ctor public TextUnit(long packedValue);
+    method public inline operator int compareTo(androidx.ui.core.TextUnit other);
+    method public long component1();
+    method public androidx.ui.core.TextUnit copy(long packedValue);
+    method public inline operator androidx.ui.core.TextUnit div(float other);
+    method public inline operator androidx.ui.core.TextUnit div(double other);
+    method public inline operator androidx.ui.core.TextUnit div(int other);
+    method public inline operator float div(androidx.ui.core.TextUnit other);
+    method public long getPackedValue();
+    method public androidx.ui.core.TextUnitType getType();
     method public float getValue();
-    method public inline operator androidx.ui.core.Sp minus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp plus(androidx.ui.core.Sp other);
-    method public inline operator androidx.ui.core.Sp times(float other);
-    method public inline operator androidx.ui.core.Sp times(int other);
-    method public inline operator androidx.ui.core.Sp unaryMinus();
-    field public static final androidx.ui.core.Sp.Companion! Companion;
+    method public boolean isEm();
+    method public boolean isInherit();
+    method public boolean isSp();
+    method public inline operator androidx.ui.core.TextUnit minus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit plus(androidx.ui.core.TextUnit other);
+    method public inline operator androidx.ui.core.TextUnit times(float other);
+    method public inline operator androidx.ui.core.TextUnit times(double other);
+    method public inline operator androidx.ui.core.TextUnit times(int other);
+    method public inline operator androidx.ui.core.TextUnit unaryMinus();
+    property public final boolean isEm;
+    property public final boolean isInherit;
+    property public final boolean isSp;
+    property public final androidx.ui.core.TextUnitType type;
+    property public final float value;
+    field public static final androidx.ui.core.TextUnit.Companion! Companion;
   }
 
-  public static final class Sp.Companion {
-    method public androidx.ui.core.Sp getInfinity();
-    method public androidx.ui.core.Sp getInherit();
-    property public final androidx.ui.core.Sp Infinity;
-    property public final androidx.ui.core.Sp Inherit;
+  public static final class TextUnit.Companion {
+    method public androidx.ui.core.TextUnit Em(int value);
+    method public androidx.ui.core.TextUnit Em(float value);
+    method public androidx.ui.core.TextUnit Em(double value);
+    method public androidx.ui.core.TextUnit Sp(int value);
+    method public androidx.ui.core.TextUnit Sp(float value);
+    method public androidx.ui.core.TextUnit Sp(double value);
+    method public androidx.ui.core.TextUnit getInherit();
+    property public final androidx.ui.core.TextUnit Inherit;
   }
 
-  public final class SpKt {
-    ctor public SpKt();
-    method public static inline androidx.ui.core.Sp coerceAtLeast(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue);
-    method public static inline androidx.ui.core.Sp coerceAtMost(androidx.ui.core.Sp, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp coerceIn(androidx.ui.core.Sp, androidx.ui.core.Sp minimumValue, androidx.ui.core.Sp maximumValue);
-    method public static inline androidx.ui.core.Sp getSp(int);
-    method public static inline androidx.ui.core.Sp getSp(double);
-    method public static inline androidx.ui.core.Sp getSp(float);
-    method public static inline boolean isInherit(androidx.ui.core.Sp);
-    method public static androidx.ui.core.Sp lerp(androidx.ui.core.Sp start, androidx.ui.core.Sp stop, float fraction);
-    method public static inline androidx.ui.core.Sp max(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline androidx.ui.core.Sp min(androidx.ui.core.Sp a, androidx.ui.core.Sp b);
-    method public static inline operator androidx.ui.core.Sp times(float, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(double, androidx.ui.core.Sp other);
-    method public static inline operator androidx.ui.core.Sp times(int, androidx.ui.core.Sp other);
+  public final class TextUnitKt {
+    ctor public TextUnitKt();
+    method public static inline androidx.ui.core.TextUnit coerceAtLeast(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue);
+    method public static inline androidx.ui.core.TextUnit coerceAtMost(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit maximumValue);
+    method public static inline androidx.ui.core.TextUnit coerceIn(androidx.ui.core.TextUnit, androidx.ui.core.TextUnit minimumValue, androidx.ui.core.TextUnit maximumValue);
+    method public static androidx.ui.core.TextUnit getEm(float);
+    method public static androidx.ui.core.TextUnit getEm(double);
+    method public static androidx.ui.core.TextUnit getEm(int);
+    method public static androidx.ui.core.TextUnit getSp(float);
+    method public static androidx.ui.core.TextUnit getSp(double);
+    method public static androidx.ui.core.TextUnit getSp(int);
+    method public static androidx.ui.core.TextUnit lerpTextUnit(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b, float t);
+    method public static inline androidx.ui.core.TextUnit max(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline androidx.ui.core.TextUnit min(androidx.ui.core.TextUnit a, androidx.ui.core.TextUnit b);
+    method public static inline operator androidx.ui.core.TextUnit times(float, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(double, androidx.ui.core.TextUnit other);
+    method public static inline operator androidx.ui.core.TextUnit times(int, androidx.ui.core.TextUnit other);
+  }
+
+  public enum TextUnitType {
+    enum_constant public static final androidx.ui.core.TextUnitType Em;
+    enum_constant public static final androidx.ui.core.TextUnitType Inherit;
+    enum_constant public static final androidx.ui.core.TextUnitType Sp;
   }
 
   public final class Timestamp implements java.lang.Comparable<androidx.ui.core.Timestamp> {
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Density.kt b/ui/ui-core/src/main/java/androidx/ui/core/Density.kt
index 30019c5..5825ea0 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Density.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Density.kt
@@ -68,24 +68,32 @@
     fun Dp.toIntPx(): IntPx = toPx().round()
 
     /**
-     * Convert [Dp] to [Sp]. [Sp] is used for font size, etc.
+     * Convert [Dp] to Sp. Sp is used for font size, etc.
      */
-    fun Dp.toSp(): Sp = Sp(value / density.fontScale)
+    fun Dp.toSp(): TextUnit = TextUnit.Sp(value / density.fontScale)
 
     /**
-     * Convert [Sp] to [Px]. Pixels are used to paint to [Canvas].
+     * Convert Sp to [Px]. Pixels are used to paint to [Canvas].
+     * @throws ArithmeticException if TextUnit other than SP unit is specified.
      */
-    fun Sp.toPx(): Px = Px(value * density.fontScale * density.density)
+    fun TextUnit.toPx(): Px {
+        require(type == TextUnitType.Sp) { "Only Sp can convert to Px" }
+        return Px(value * density.fontScale * density.density)
+    }
 
     /**
-     * Convert [Sp] to [IntPx] by rounding
+     * Convert Sp to [IntPx] by rounding
      */
-    fun Sp.toIntPx(): IntPx = toPx().round()
+    fun TextUnit.toIntPx(): IntPx = toPx().round()
 
     /**
-     * Convert [Sp] to [Dp].
+     * Convert Sp to [Dp].
+     * @throws ArithmeticException if TextUnit other than SP unit is specified.
      */
-    fun Sp.toDp(): Dp = Dp(value * density.fontScale)
+    fun TextUnit.toDp(): Dp {
+        require(type == TextUnitType.Sp) { "Only Sp can convert to Px" }
+        return Dp(value * density.fontScale)
+    }
 
     /**
      * Convert [Px] to [Dp].
@@ -93,9 +101,9 @@
     fun Px.toDp(): Dp = (value / density.density).dp
 
     /**
-     * Convert [Px] to [Sp].
+     * Convert [Px] to Sp.
      */
-    fun Px.toSp(): Sp = (value / (density.fontScale * density.density)).sp
+    fun Px.toSp(): TextUnit = (value / (density.fontScale * density.density)).sp
 
     /**
      * Convert [IntPx] to [Dp].
@@ -103,21 +111,21 @@
     fun IntPx.toDp(): Dp = (value / density.density).dp
 
     /**
-     * Convert [IntPx] to [Sp].
+     * Convert [IntPx] to Sp.
      */
-    fun IntPx.toSp(): Sp = (value / (density.fontScale * density.density)).sp
+    fun IntPx.toSp(): TextUnit = (value / (density.fontScale * density.density)).sp
 
     /** Convert a [Float] pixel value to a Dp */
     fun Float.toDp(): Dp = (this / density.density).dp
 
     /** Convert a [Float] pixel value to a Sp */
-    fun Float.toSp(): Sp = (this / (density.fontScale * density.density)).sp
+    fun Float.toSp(): TextUnit = (this / (density.fontScale * density.density)).sp
 
     /** Convert a [Int] pixel value to a Dp */
     fun Int.toDp(): Dp = toFloat().toDp()
 
     /** Convert a [Int] pixel value to a Sp */
-    fun Int.toSp(): Sp = toFloat().toSp()
+    fun Int.toSp(): TextUnit = toFloat().toSp()
 
     /**
      * Convert a [Size] to a [PxSize].
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Em.kt b/ui/ui-core/src/main/java/androidx/ui/core/Em.kt
deleted file mode 100644
index fea4caf..0000000
--- a/ui/ui-core/src/main/java/androidx/ui/core/Em.kt
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * 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.
- */
-@file:Suppress("NOTHING_TO_INLINE")
-
-package androidx.ui.core
-
-import androidx.compose.Immutable
-import kotlin.math.max
-import kotlin.math.min
-
-/**
- * Dimension value representing Em. 1 Em is defined to be the font size when doing the text layout.
- * For example:
- *     Text(style = TextStyle(fontSize = 20.sp, letterSpacing = 0.2.em)
- * The letter spacing rendered equals to 4.sp = 20.sp * 0.2em.
- *
- * Em is used only in typography related APIs. Since it's a relative length unit, the
- * change of font size will also influence the computed Em length.
- * For example:
- *     Text {
- *         Span(style = TextStyle(letterSpacing = 0.5.em) {
- *             // letter spacing for "Hello" equals to 7.sp
- *             Span(text = "Hello", style = TextStyle(fontSize = 14.sp))
- *             // letter spacing for "World" equals to 9.sp
- *             Span(text = "World", style = TextStyle(fontSize = 18.sp))
- *         }
- *     }
- */
-@Immutable
-data /*inline*/ class Em(val value: Float) {
-    /**
-     * Add two [Em]s together.
-     */
-    inline operator fun plus(other: Em) =
-        Em(value = this.value + other.value)
-
-    /**
-     * Subtract a Em from another one.
-     */
-    inline operator fun minus(other: Em) =
-        Em(value = this.value - other.value)
-
-    /**
-     * This is the same as multiplying the Em by -1.0.
-     */
-    inline operator fun unaryMinus() = Em(-value)
-
-    /**
-     * Divide a Em by a scalar.
-     */
-    inline operator fun div(other: Float): Em =
-        Em(value = value / other)
-
-    inline operator fun div(other: Int): Em =
-        Em(value = value / other)
-
-    /**
-     * Divide by another Em to get a scalar.
-     */
-    inline operator fun div(other: Em): Float = value / other.value
-
-    /**
-     * Multiply a Em by a scalar.
-     */
-    inline operator fun times(other: Float): Em =
-        Em(value = value * other)
-
-    inline operator fun times(other: Int): Em =
-        Em(value = value * other)
-
-    /**
-     * Support comparing Dimensions with comparison operators.
-     */
-    inline operator fun compareTo(other: Em) = value.compareTo(other.value)
-}
-
-/**
- * Create a [Em] using an [Int]:
- *     val letterSpacing = 1
- *     val x = letterSpacing.em
- *     // -- or --
- *     val y = 1.em
- */
-inline val Int.em: Em get() = Em(value = this.toFloat())
-
-/**
- * Create a [Em] using a [Double]:
- *     val letterSpacing = 1.0
- *     val x = letterSpacing.em
- *     // -- or --
- *     val y = 1.0.em
- */
-inline val Double.em: Em get() = Em(value = this.toFloat())
-
-/**
- * Create a [Em] using a [Float]:
- *     val letterSpacing = 1f
- *     val x = letterSpacing.em
- *     // -- or --
- *     val y = 1f.em
- */
-inline val Float.em: Em get() = Em(value = this)
-
-inline operator fun Float.times(other: Em) =
-    Em(this * other.value)
-
-inline operator fun Double.times(other: Em) =
-    Em(this.toFloat() * other.value)
-
-inline operator fun Int.times(other: Em) =
-    Em(this * other.value)
-
-inline fun min(a: Em, b: Em): Em = Em(value = min(a.value, b.value))
-
-inline fun max(a: Em, b: Em): Em = Em(value = max(a.value, b.value))
-
-/**
- * Ensures that this value lies in the specified range [minimumValue]..[maximumValue].
- *
- * @return this value if it's in the range, or [minimumValue] if this value is less than
- * [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
- */
-inline fun Em.coerceIn(minimumValue: Em, maximumValue: Em): Em =
-    Em(value = value.coerceIn(minimumValue.value, maximumValue.value))
-
-/**
- * Ensures that this value is not less than the specified [minimumValue].
- *
- * @return this value if it's greater than or equal to the [minimumValue] or the
- * [minimumValue] otherwise.
- */
-inline fun Em.coerceAtLeast(minimumValue: Em): Em =
-    Em(value = value.coerceAtLeast(minimumValue.value))
-
-/**
- * Ensures that this value is not greater than the specified [maximumValue].
- *
- * @return this value if it's less than or equal to the [maximumValue] or the
- * [maximumValue] otherwise.
- */
-inline fun Em.coerceAtMost(maximumValue: Em): Em =
-    Em(value = value.coerceAtMost(maximumValue.value))
-
-/**
- * Convert a value of [Sp] to [Em].
- *
- * @param fontSize the context font size where [Em] is used.
- */
-inline fun Sp.toEm(fontSize: Sp): Em = Em(value = value / fontSize.value)
-
-/**
- * Linearly interpolate between two [Em]s.
- *
- * The [fraction] argument represents position on the timeline, with 0.0 meaning
- * that the interpolation has not started, returning [start] (or something
- * equivalent to [start]), 1.0 meaning that the interpolation has finished,
- * returning [stop] (or something equivalent to [stop]), and values in between
- * meaning that the interpolation is at the relevant point on the timeline
- * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
- * 1.0, so negative values and values greater than 1.0 are valid.
- */
-fun lerp(start: Em, stop: Em, fraction: Float): Em {
-    return Em(androidx.ui.lerp(start.value, stop.value, fraction))
-}
\ No newline at end of file
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Sp.kt b/ui/ui-core/src/main/java/androidx/ui/core/Sp.kt
deleted file mode 100644
index 6668aa7..0000000
--- a/ui/ui-core/src/main/java/androidx/ui/core/Sp.kt
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * 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.
- */
-@file:Suppress("NOTHING_TO_INLINE")
-
-package androidx.ui.core
-
-import androidx.compose.Immutable
-import kotlin.math.max
-import kotlin.math.min
-
-/**
- * Dimension value representing scaled pixels (sp). Font related APIs specify their
- * dimensions such as font size in SP with Sp objects. Sp are normally
- * defined using [sp], which can be applied to [Int], [Double], and [Float].
- *     val leftMargin = 10.sp
- *     val rightMargin = 10f.sp
- *     val topMargin = 20.0.sp
- *     val bottomMargin = 10.sp
- * Drawing is done in pixels. To retrieve the pixel size of a Dp, use [toPx]:
- *     val lineThicknessPx = lineThickness.toPx(context)
- * [toPx] is normally needed only for painting operations.
- */
-@Suppress("EXPERIMENTAL_FEATURE_WARNING")
-@Immutable
-data /*inline*/ class Sp(val value: Float) {
-    /**
-     * Add two [Sp]s together.
-     */
-    inline operator fun plus(other: Sp) = checkNotInherit(this, other) {
-        Sp(value = this.value + other.value)
-    }
-
-    /**
-     * Subtract a Sp from another one.
-     */
-    inline operator fun minus(other: Sp) = checkNotInherit(this, other) {
-        Sp(value = this.value - other.value)
-    }
-
-    /**
-     * This is the same as multiplying the Sp by -1.0.
-     */
-    inline operator fun unaryMinus() = checkNotInherit(this) { Sp(-value) }
-
-    /**
-     * Divide a Sp by a scalar.
-     */
-    inline operator fun div(other: Float): Sp = checkNotInherit(this) {
-        Sp(value = value / other)
-    }
-
-    inline operator fun div(other: Int): Sp = checkNotInherit(this) {
-        Sp(value = value / other)
-    }
-
-    /**
-     * Divide by another Sp to get a scalar.
-     */
-    inline operator fun div(other: Sp): Float = checkNotInherit(this, other) {
-        value / other.value
-    }
-
-    /**
-     * Multiply a Sp by a scalar.
-     */
-    inline operator fun times(other: Float): Sp = checkNotInherit(this) {
-        Sp(value = value * other)
-    }
-
-    inline operator fun times(other: Int): Sp = checkNotInherit(this) {
-        Sp(value = value * other)
-    }
-
-    /**
-     * Support comparing Dimensions with comparison operators.
-     */
-    inline operator fun compareTo(other: Sp) = value.compareTo(other.value)
-
-    companion object {
-        /**
-         * Infinite Sp dimension.
-         */
-        val Infinity = Sp(value = Float.POSITIVE_INFINITY)
-
-        /**
-         * A special Sp instance for representing inheriting from the parent value.
-         *
-         * Do not set this value unless the API doc explicitly mentioned that [Sp.Inherit] is
-         * allowed.
-         */
-        val Inherit = Sp(value = Float.NEGATIVE_INFINITY)
-    }
-}
-
-/**
- * Create a [Sp] using an [Int]:
- *     val fontSize = 10
- *     val x = fontSize.sp
- *     // -- or --
- *     val y = 10.sp
- */
-inline val Int.sp: Sp get() = Sp(value = this.toFloat())
-
-/**
- * Create a [Sp] using a [Double]:
- *     val fontSize = 10.0
- *     val x = fontSize.sp
- *     // -- or --
- *     val y = 10.0.sp
- */
-inline val Double.sp: Sp get() = Sp(value = this.toFloat())
-
-/**
- * Create a [Sp] using a [Float]:
- *     val fontSize = 10f
- *     val x = fontSize.sp
- *     // -- or --
- *     val y = 10f.sp
- */
-inline val Float.sp: Sp get() = Sp(value = this)
-
-inline operator fun Float.times(other: Sp) = checkNotInherit(other) {
-    Sp(this * other.value)
-}
-
-inline operator fun Double.times(other: Sp) = checkNotInherit(other) {
-    Sp(this.toFloat() * other.value)
-}
-
-inline operator fun Int.times(other: Sp) = checkNotInherit(other) {
-    Sp(this * other.value)
-}
-
-inline fun min(a: Sp, b: Sp): Sp = checkNotInherit(a, b) {
-    Sp(value = min(a.value, b.value))
-}
-
-inline fun max(a: Sp, b: Sp): Sp = checkNotInherit(a, b) {
-    Sp(value = max(a.value, b.value))
-}
-
-/**
- * Ensures that this value lies in the specified range [minimumValue]..[maximumValue].
- *
- * @return this value if it's in the range, or [minimumValue] if this value is less than
- * [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
- */
-inline fun Sp.coerceIn(minimumValue: Sp, maximumValue: Sp): Sp =
-    checkNotInherit(this, minimumValue, maximumValue) {
-        Sp(value = value.coerceIn(minimumValue.value, maximumValue.value))
-    }
-
-/**
- * Ensures that this value is not less than the specified [minimumValue].
- *
- * @return this value if it's greater than or equal to the [minimumValue] or the
- * [minimumValue] otherwise.
- */
-inline fun Sp.coerceAtLeast(minimumValue: Sp): Sp = checkNotInherit(this, minimumValue) {
-    Sp(value = value.coerceAtLeast(minimumValue.value))
-}
-
-/**
- * Ensures that this value is not greater than the specified [maximumValue].
- *
- * @return this value if it's less than or equal to the [maximumValue] or the
- * [maximumValue] otherwise.
- */
-inline fun Sp.coerceAtMost(maximumValue: Sp): Sp = checkNotInherit(this, maximumValue) {
-    Sp(value = value.coerceAtMost(maximumValue.value))
-}
-
-/**
- * Returns true if this value is [Sp.Inherit]
- */
-inline fun Sp.isInherit(): Boolean = this.value == Float.NEGATIVE_INFINITY
-
-@PublishedApi
-internal inline fun <T> checkNotInherit(a: Sp, block: () -> T): T {
-    if (a.isInherit()) throw IllegalArgumentException("Cannot perform operation for Sp.Inherit")
-    return block()
-}
-
-@PublishedApi
-internal inline fun <T> checkNotInherit(a: Sp, b: Sp, block: () -> T): T {
-    if (a.isInherit() || b.isInherit())
-        throw IllegalArgumentException("Cannot perform operation for Sp.Inherit")
-    return block()
-}
-
-@PublishedApi
-internal inline fun <T> checkNotInherit(a: Sp, b: Sp, c: Sp, block: () -> T): T {
-    if (a.isInherit() || b.isInherit() || c.isInherit())
-        throw IllegalArgumentException("Cannot perform operation for Sp.Inherit")
-    return block()
-}
-
-/**
- * Linearly interpolate between two [Sp]s.
- *
- * The [fraction] argument represents position on the timeline, with 0.0 meaning
- * that the interpolation has not started, returning [start] (or something
- * equivalent to [start]), 1.0 meaning that the interpolation has finished,
- * returning [stop] (or something equivalent to [stop]), and values in between
- * meaning that the interpolation is at the relevant point on the timeline
- * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
- * 1.0, so negative values and values greater than 1.0 are valid.
- */
-fun lerp(start: Sp, stop: Sp, fraction: Float): Sp = checkNotInherit(start, stop) {
-    Sp(androidx.ui.lerp(start.value, stop.value, fraction))
-}
\ No newline at end of file
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/TextUnit.kt b/ui/ui-core/src/main/java/androidx/ui/core/TextUnit.kt
new file mode 100644
index 0000000..a112153
--- /dev/null
+++ b/ui/ui-core/src/main/java/androidx/ui/core/TextUnit.kt
@@ -0,0 +1,400 @@
+/*
+ * 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.
+ */
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.ui.core
+
+import androidx.compose.Immutable
+
+/**
+ * We encode the unit information and float value into the single 64-bit long integer.
+ * The higher 32bit represents the metadata of this value and lower 32bit represents the bit
+ * representation of the float value. Currently lower 8bits in the metadata bits are used for
+ * unit information.
+ *
+ * Bits
+ * |-------|-------|-------|-------|-------|-------|-------|-------|
+ *                                  FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF: Float Value
+ *                          UUUUUUUU                                : Unit Information
+ *  XXXXXXXXXXXXXXXXXXXXXXXX                                        : Unused bits
+ */
+private const val UNIT_MASK = 0xFFL shl 32 // 0xFF_0000_0000
+private const val UNIT_TYPE_INHERIT = 0x00L shl 32 // 0x00_0000_0000
+private const val UNIT_TYPE_SP = 0x01L shl 32 // 0x01_0000_0000
+private const val UNIT_TYPE_EM = 0x02L shl 32 // 0x2_0000_0000
+
+/**
+ * An enum class defining for type of thextUnit.
+ */
+enum class TextUnitType { Inherit, Sp, Em }
+
+/**
+ * The unit used for text related dimension value.
+ *
+ * This unit can hold either scaled pixels (SP), relative font size (em) and special unit for
+ * indicating inheriting from other style.
+ *
+ * Note that do not store this value in your persistent storage or send to another process since
+ * the internal representation may be changed in future.
+ */
+@Suppress("EXPERIMENTAL_FEATURE_WARNING")
+@Immutable
+data /*inline*/ class TextUnit(val packedValue: Long) {
+    /**
+     * Add two [TextUnit]s together.
+     *
+     * This operation works only if all the operands are the same unit type and not they are not
+     * equal to [TextUnit.Inherit].
+     * The result of this operation is the same unit type of the given one.
+     */
+    inline operator fun plus(other: TextUnit) = checkArithmetic(this, other) {
+        pack(rawType, value + other.value)
+    }
+
+    /**
+     * Subtract a [TextUnit] from another one.
+
+     * This operation works only if all the operands are the same unit type and not they are not
+     * equal to [TextUnit.Inherit].
+     * The result of this operation is the same unit type of the given one.
+     */
+    inline operator fun minus(other: TextUnit) = checkArithmetic(this, other) {
+        pack(rawType, value - other.value)
+    }
+
+    /**
+     * This is the same as multiplying the [TextUnit] by -1.0.
+     *
+     * This operation works only if the operand is not equal to [TextUnit.Inherit].
+     * The result of this operation is the same unit type of the given one.
+     */
+    inline operator fun unaryMinus() = checkArithmetic(this) {
+        pack(rawType, -value)
+    }
+
+    /**
+     * Divide a [TextUnit] by a scalar.
+     *
+     * This operation works only if the left operand is not equal to [TextUnit.Inherit].
+     * The result of this operation is the same unit type of the given one.
+     */
+    inline operator fun div(other: Float): TextUnit = checkArithmetic(this) {
+        pack(rawType, value / other)
+    }
+
+    /**
+     * Divide a [TextUnit] by a scalar.
+     *
+     * This operation works only if the left operand is not equal to [TextUnit.Inherit].
+     * The result of this operation is the same unit type of the given one.
+     */
+    inline operator fun div(other: Double): TextUnit = checkArithmetic(this) {
+        pack(rawType, (value / other).toFloat())
+    }
+
+    /**
+     * Divide a [TextUnit] by a scalar.
+     *
+     * This operation works only if the left operand is not equal to [TextUnit.Inherit].
+     * The result of this operation is the same unit type of the given one.
+     */
+    inline operator fun div(other: Int): TextUnit = checkArithmetic(this) {
+        pack(rawType, value / other)
+    }
+
+    /**
+     * Divide by another [TextUnit] to get a scalar.
+     *
+     * This operation works only if all the operands are the same unit type and they are not
+     * equal to [TextUnit.Inherit].
+     */
+    inline operator fun div(other: TextUnit): Float = checkArithmetic(this, other) {
+        value / other.value
+    }
+
+    /**
+     * Multiply a [TextUnit] by a scalar.
+     *
+     * This operation works only if the left operand is not equal to [TextUnit.Inherit].
+     * The result of this operation is the same unit type of the given one.
+     */
+    inline operator fun times(other: Float): TextUnit = checkArithmetic(this) {
+        pack(rawType, value * other)
+    }
+
+    /**
+     * Multiply a [TextUnit] by a scalar.
+     *
+     * This operation works only if the left operand is not equal to [TextUnit.Inherit].
+     * The result of this operation is the same unit type of the given one.
+     */
+    inline operator fun times(other: Double): TextUnit = checkArithmetic(this) {
+        pack(rawType, (value * other).toFloat())
+    }
+
+    /**
+     * Multiply a [TextUnit] by a scalar.
+     *
+     * This operation works only if the left operand is not equal to [TextUnit.Inherit].
+     * The result of this operation is the same unit type of the given one.
+     */
+    inline operator fun times(other: Int): TextUnit = checkArithmetic(this) {
+        pack(rawType, value * other)
+    }
+
+    /**
+     * Support comparing Dimensions with comparison operators.
+     */
+    inline operator fun compareTo(other: TextUnit): Int = checkArithmetic(this, other) {
+        value.compareTo(other.value)
+    }
+
+    companion object {
+        /**
+         * Creates a SP unit [TextUnit].
+         */
+        fun Sp(value: Int) = pack(UNIT_TYPE_SP, value.toFloat())
+
+        /**
+         * Creates a SP unit [TextUnit].
+         */
+        fun Sp(value: Float) = pack(UNIT_TYPE_SP, value)
+
+        /**
+         * Creates a SP unit [TextUnit].
+         */
+        fun Sp(value: Double) = pack(UNIT_TYPE_SP, value.toFloat())
+
+        /**
+         * Creates an EM unit [TextUnit].
+         */
+        fun Em(value: Int) = pack(UNIT_TYPE_EM, value.toFloat())
+
+        /**
+         * Creates an EM unit [TextUnit].
+         */
+        fun Em(value: Float) = pack(UNIT_TYPE_EM, value)
+
+        /**
+         * Creates an EM unit [TextUnit].
+         */
+        fun Em(value: Double) = pack(UNIT_TYPE_EM, value.toFloat())
+
+        /**
+         * A special [TextUnit] instance for representing inheriting from parent value.
+         */
+        val Inherit = pack(UNIT_TYPE_INHERIT, 0f)
+    }
+
+    /**
+     * A helper function for getting underlying type information in raw bits.
+     *
+     * Use [TextUnit.type] in public places.
+     */
+    @PublishedApi
+    internal val rawType: Long get() = packedValue and UNIT_MASK
+
+    /**
+     * A type information of this TextUnit.
+     *
+     * @throws RuntimeException if unknown unknown unit type is appeared.
+     */
+    val type: TextUnitType get() = when (rawType) {
+        UNIT_TYPE_INHERIT -> TextUnitType.Inherit
+        UNIT_TYPE_SP -> TextUnitType.Sp
+        UNIT_TYPE_EM -> TextUnitType.Em
+        else -> throw RuntimeException("packed TextUnit has unknown unit type.")
+    }
+
+    /**
+     * True if this is [TextUnit.Inherit], otherwise false.
+     */
+    val isInherit get() = rawType == UNIT_TYPE_INHERIT
+
+    /**
+     * True if this is a SP unit type.
+     */
+    val isSp get() = rawType == UNIT_TYPE_SP
+
+    /**
+     * True if this is a EM unit type.
+     */
+    val isEm get() = rawType == UNIT_TYPE_EM
+
+    /**
+     * Returns the value
+     */
+    val value get() = Float.fromBits((packedValue and 0xFFFF_FFFFL).toInt())
+}
+
+/**
+ * Creates a SP unit [TextUnit]
+ */
+val Float.sp: TextUnit get() = pack(UNIT_TYPE_SP, this)
+
+/**
+ * Creates an EM unit [TextUnit]
+ */
+val Float.em: TextUnit get() = pack(UNIT_TYPE_EM, this)
+
+/**
+ * Creates a SP unit [TextUnit]
+ */
+val Double.sp: TextUnit get() = pack(UNIT_TYPE_SP, this.toFloat())
+
+/**
+ * Creates an EM unit [TextUnit]
+ */
+val Double.em: TextUnit get() = pack(UNIT_TYPE_EM, this.toFloat())
+
+/**
+ * Creates a SP unit [TextUnit]
+ */
+val Int.sp: TextUnit get() = pack(UNIT_TYPE_SP, this.toFloat())
+
+/**
+ * Creates an EM unit [TextUnit]
+ */
+val Int.em: TextUnit get() = pack(UNIT_TYPE_EM, this.toFloat())
+
+/**
+ * Multiply a [TextUnit] by a scalar.
+ *
+ * This operation works only if the right operand is not equal to [TextUnit.Inherit].
+ * The result of this operation is the same unit type of the given one.
+ */
+inline operator fun Float.times(other: TextUnit) = checkArithmetic(other) {
+    pack(other.rawType, this * other.value)
+}
+
+/**
+ * Multiply a [TextUnit] by a scalar.
+ *
+ * This operation works only if the right operand is not equal to [TextUnit.Inherit].
+ * The result of this operation is the same unit type of the given one.
+ */
+inline operator fun Double.times(other: TextUnit) = checkArithmetic(other) {
+    pack(other.rawType, this.toFloat() * other.value)
+}
+
+/**
+ * Multiply a [TextUnit] by a scalar.
+ *
+ * This operation works only if the right operand is not equal to [TextUnit.Inherit].
+ * The result of this operation is the same unit type of the given one.
+ */
+inline operator fun Int.times(other: TextUnit) = checkArithmetic(other) {
+    pack(other.rawType, this * other.value)
+}
+
+/**
+ * Returns the smaller value from the given values.
+ *
+ * This operation works only if all the operands are the same unit type and they are not
+ * equal to [TextUnit.Inherit].
+ * The result of this operation is the same unit type of the given one.
+ */
+inline fun min(a: TextUnit, b: TextUnit): TextUnit = checkArithmetic(a, b) {
+    if (a.value < b.value) a else b
+}
+
+/**
+ * Returns the smaller value from the given values.
+ *
+ * This operation works only if all the operands are the same unit type and they are not
+ * equal to [TextUnit.Inherit].
+ * The result of this operation is the same unit type of the given one.
+ */
+inline fun max(a: TextUnit, b: TextUnit): TextUnit = checkArithmetic(a, b) {
+    if (a.value < b.value) b else a
+}
+
+/**
+ * Ensures that the value of [TextUnit] lies in the specified range [minimumValue]..[maximumValue].
+ *
+ *
+ * This operation works only if all the operands are the same unit type and they are not
+ * equal to [TextUnit.Inherit].
+ * The result of this operation is the same unit type of the given one.
+ *
+ * @return this value if it's in the range, or [minimumValue] if this value is less than
+ * [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
+ */
+inline fun TextUnit.coerceIn(minimumValue: TextUnit, maximumValue: TextUnit): TextUnit =
+    checkArithmetic(this, minimumValue, maximumValue) {
+        pack(rawType, value.coerceIn(minimumValue.value, maximumValue.value))
+    }
+
+/**
+ * Ensures that the value of [TextUnit] is not less than the specified [minimumValue].
+ *
+ * @return this value if it's greater than or equal to the [minimumValue] or the
+ * [minimumValue] otherwise.
+ */
+inline fun TextUnit.coerceAtLeast(minimumValue: TextUnit): TextUnit =
+    checkArithmetic(this, minimumValue) {
+        pack(rawType, value.coerceAtLeast(minimumValue.value))
+    }
+
+/**
+ * Ensures that the value of [TextUnit] is not greater than the specified [maximumValue].
+ *
+ * @return this value if it's less than or equal to the [maximumValue] or the
+ * [maximumValue] otherwise.
+ */
+inline fun TextUnit.coerceAtMost(maximumValue: TextUnit): TextUnit =
+    checkArithmetic(this, maximumValue) {
+        pack(rawType, value.coerceAtMost(maximumValue.value))
+    }
+
+@PublishedApi
+internal inline fun pack(unitType: Long, v: Float): TextUnit =
+    TextUnit(unitType or (v.toBits().toLong() and 0xFFFF_FFFFL))
+
+@PublishedApi
+internal inline fun <T> checkArithmetic(a: TextUnit, block: () -> T): T {
+    require(a.type != TextUnitType.Inherit) {
+        "Cannot perform operation for Inherit type."
+    }
+    return block()
+}
+
+@PublishedApi
+internal inline fun <T> checkArithmetic(a: TextUnit, b: TextUnit, block: () -> T): T {
+    require(a.type != TextUnitType.Inherit && b.type != TextUnitType.Inherit) {
+        "Cannot perform operation for Inherit type."
+    }
+    require(a.type == b.type) {
+        "Cannot perform operation for ${a.type} and ${b.type}"
+    }
+    return block()
+}
+
+@PublishedApi
+internal inline fun <T> checkArithmetic(a: TextUnit, b: TextUnit, c: TextUnit, block: () -> T): T {
+    require(a.type != TextUnitType.Inherit && b.type != TextUnitType.Inherit &&
+            c.type != TextUnitType.Inherit) {
+        "Cannot perform operation for Inherit type."
+    }
+    require(a.type == b.type && b.type == c.type) {
+        "Cannot perform operation for ${a.type} and ${b.type}"
+    }
+    return block()
+}
+
+fun lerpTextUnit(a: TextUnit, b: TextUnit, t: Float): TextUnit = checkArithmetic(a, b) {
+    return pack(a.rawType, a.value + (b.value - a.value) * t)
+}
diff --git a/ui/ui-core/src/test/java/androidx/ui/core/EmTest.kt b/ui/ui-core/src/test/java/androidx/ui/core/EmTest.kt
deleted file mode 100644
index 4e690b7..0000000
--- a/ui/ui-core/src/test/java/androidx/ui/core/EmTest.kt
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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.ui.core
-
-import com.google.common.truth.Truth.assertThat
-import org.junit.Assert
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class EmTest {
-    @Test
-    fun constructor() {
-        val dim1 = Em(value = 5f)
-        assertThat(dim1.value).isEqualTo(5f)
-
-        val dim2 = Em(value = Float.POSITIVE_INFINITY)
-        assertThat(dim2.value).isEqualTo(Float.POSITIVE_INFINITY)
-
-        val dim3 = Em(value = Float.NaN)
-        assertThat(dim3.value).isEqualTo(Float.NaN)
-    }
-
-    @Test
-    fun emIntegerConstruction() {
-        val dim = 10.em
-        assertThat(dim.value).isEqualTo(10f)
-    }
-
-    @Test
-    fun emFloatConstruction() {
-        val dim = 10f.em
-        assertThat(dim.value).isEqualTo(10f)
-    }
-
-    @Test
-    fun emDoubleConstruction() {
-        val dim = 10.0.em
-        assertThat(dim.value).isEqualTo(10f)
-    }
-
-    @Test
-    fun unaryMinus() {
-        assertThat((-(1.em)).value).isEqualTo(-1f)
-    }
-
-    @Test
-    fun subtractOperator() {
-        assertThat((3.em - 4.em).value).isEqualTo(-1f)
-        assertThat((10.em - 9.em).value).isEqualTo(1f)
-    }
-
-    @Test
-    fun addOperator() {
-        assertThat((1.em + 1.em).value).isEqualTo(2f)
-        assertThat((6.em + 4.em).value).isEqualTo(10f)
-    }
-
-    @Test
-    fun multiplyOperator() {
-        assertThat((1.em * 0f).value).isZero()
-        assertThat((1.em * 10f).value).isEqualTo(10f)
-    }
-
-    @Test
-    fun multiplyOperatorScalar() {
-        assertThat((10f * 1.em).value).isEqualTo(10f)
-        assertThat((10 * 1.em).value).isEqualTo(10f)
-        assertThat((10.0 * 1.em).value).isEqualTo(10f)
-    }
-
-    @Test
-    fun divideOperator() {
-        assertThat((100.em / 10f).value).isEqualTo(10f)
-        assertThat((0.em / 10f).value).isZero()
-    }
-
-    @Test
-    fun divideToScalar() {
-        assertThat(1.em / 1.em).isEqualTo(1f)
-    }
-
-    @Suppress("DIVISION_BY_ZERO")
-    @Test
-    fun compare() {
-        assertThat(0.em < Float.MIN_VALUE.em).isTrue()
-        assertThat(1.em < 3.em).isTrue()
-        assertThat(1.em.compareTo(1.em)).isEqualTo(0)
-        assertThat(1.em > 0.em).isTrue()
-        assertThat(Float.NEGATIVE_INFINITY.em < 0.em).isTrue()
-
-        val zeroNaN = 0f / 0f
-        val infNaN = Float.POSITIVE_INFINITY / Float.NEGATIVE_INFINITY
-        assertThat(zeroNaN.em.compareTo(zeroNaN.em)).isEqualTo(0)
-        assertThat(infNaN.em.compareTo(infNaN.em)).isEqualTo(0)
-    }
-
-    @Test
-    fun minTest() {
-        assertThat(min(10.em, 20.em).value).isEqualTo(10f)
-        assertThat(min(20.em, 10.em).value).isEqualTo(10f)
-        assertThat(min(10.em, 10.em).value).isEqualTo(10f)
-    }
-
-    @Test
-    fun maxTest() {
-        assertThat(max(10.em, 20.em).value).isEqualTo(20f)
-        assertThat(max(20.em, 10.em).value).isEqualTo(20f)
-        assertThat(max(20.em, 20.em).value).isEqualTo(20f)
-    }
-
-    @Test
-    fun coerceIn() {
-        assertThat(10.em.coerceIn(0.em, 20.em).value).isEqualTo(10f)
-        assertThat(20.em.coerceIn(0.em, 10.em).value).isEqualTo(10f)
-        assertThat(0.em.coerceIn(10.em, 20.em).value).isEqualTo(10f)
-        try {
-            10.em.coerceIn(20.em, 10.em)
-            Assert.fail("Expected an exception here")
-        } catch (e: IllegalArgumentException) {
-            // success!
-        }
-    }
-
-    @Test
-    fun coerceAtLeast() {
-        assertThat(0.em.coerceAtLeast(10.em).value).isEqualTo(10f)
-        assertThat(10.em.coerceAtLeast(5.em).value).isEqualTo(10f)
-        assertThat(10.em.coerceAtLeast(10.em).value).isEqualTo(10f)
-    }
-
-    @Test
-    fun coerceAtMost() {
-        assertThat(100.em.coerceAtMost(10.em).value).isEqualTo(10f)
-        assertThat(10.em.coerceAtMost(20.em).value).isEqualTo(10f)
-        assertThat(10.em.coerceAtMost(10.em).value).isEqualTo(10f)
-    }
-
-    @Test
-    fun lerp() {
-        assertThat(lerp(0.em, 10.em, 1f).value).isEqualTo(10f)
-        assertThat(lerp(0.em, 10.em, 0f).value).isZero()
-        assertThat(lerp(0.em, 10.em, 0.5f).value).isEqualTo(5f)
-    }
-}
\ No newline at end of file
diff --git a/ui/ui-core/src/test/java/androidx/ui/core/SpTest.kt b/ui/ui-core/src/test/java/androidx/ui/core/SpTest.kt
deleted file mode 100644
index a5fb519..0000000
--- a/ui/ui-core/src/test/java/androidx/ui/core/SpTest.kt
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * 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.ui.core
-
-import com.google.common.truth.Truth.assertThat
-import org.junit.Assert
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class SpTest {
-    @Test
-    fun constructor() {
-        val dim1 = Sp(value = 5f)
-        Assert.assertEquals(5f, dim1.value, 0f)
-
-        val dim2 = Sp(value = Float.POSITIVE_INFINITY)
-        Assert.assertEquals(Float.POSITIVE_INFINITY, dim2.value, 0f)
-
-        val dim3 = Sp(value = Float.NaN)
-        Assert.assertEquals(Float.NaN, dim3.value, 0f)
-    }
-
-    @Test
-    fun spIntegerConstruction() {
-        val dim = 10.sp
-        Assert.assertEquals(10f, dim.value, 0f)
-    }
-
-    @Test
-    fun spFloatConstruction() {
-        val dim = 10f.sp
-        Assert.assertEquals(10f, dim.value, 0f)
-    }
-
-    @Test
-    fun spDoubleConstruction() {
-        val dim = 10.0.sp
-        Assert.assertEquals(10f, dim.value, 0f)
-    }
-
-    @Test
-    fun subtractOperator() {
-        Assert.assertEquals(-1f, (3.sp - 4.sp).value)
-        Assert.assertEquals(1f, (10.sp - 9.sp).value, 0f)
-    }
-
-    @Test
-    fun addOperator() {
-        Assert.assertEquals(2f, (1.sp + 1.sp).value, 0f)
-        Assert.assertEquals(10f, (6.sp + 4.sp).value, 0f)
-    }
-
-    @Test
-    fun multiplyOperator() {
-        Assert.assertEquals(0f, (1.sp * 0f).value, 0f)
-        Assert.assertEquals(10f, (1.sp * 10f).value, 0f)
-    }
-
-    @Test
-    fun multiplyOperatorScalar() {
-        Assert.assertEquals(10f, (10f * 1.sp).value, 0f)
-        Assert.assertEquals(10f, (10 * 1.sp).value, 0f)
-        Assert.assertEquals(10f, (10.0 * 1.sp).value, 0f)
-    }
-
-    @Test
-    fun divideOperator() {
-        Assert.assertEquals(10f, (100.sp / 10f).value, 0f)
-        Assert.assertEquals(0f, (0.sp / 10f).value, 0f)
-    }
-
-    @Test
-    fun divideToScalar() {
-        Assert.assertEquals(1f, 1.sp / 1.sp, 0f)
-    }
-
-    @Test
-    fun infinite() {
-        Assert.assertEquals(Float.POSITIVE_INFINITY, Sp.Infinity.value, 0f)
-    }
-
-    @Suppress("DIVISION_BY_ZERO")
-    @Test
-    fun compare() {
-        Assert.assertTrue(0.sp < Float.MIN_VALUE.sp)
-        Assert.assertTrue(1.sp < 3.sp)
-        Assert.assertEquals(0, 1.sp.compareTo(1.sp))
-        Assert.assertTrue(1.sp > 0.sp)
-        Assert.assertTrue(Float.NEGATIVE_INFINITY.sp < Sp.Infinity)
-        Assert.assertTrue(Float.NEGATIVE_INFINITY.sp < 0.sp)
-        Assert.assertTrue(Sp.Infinity > Float.MAX_VALUE.sp)
-
-        val zeroNaN = 0f / 0f
-        val infNaN = Float.POSITIVE_INFINITY / Float.NEGATIVE_INFINITY
-        Assert.assertEquals(0, zeroNaN.sp.compareTo(zeroNaN.sp))
-        Assert.assertEquals(0, infNaN.sp.compareTo(infNaN.sp))
-    }
-
-    @Test
-    fun minTest() {
-        Assert.assertEquals(10f, min(10.sp, 20.sp).value, 0f)
-        Assert.assertEquals(10f, min(20.sp, 10.sp).value, 0f)
-        Assert.assertEquals(10f, min(10.sp, 10.sp).value, 0f)
-    }
-
-    @Test
-    fun maxTest() {
-        Assert.assertEquals(20f, max(10.sp, 20.sp).value, 0f)
-        Assert.assertEquals(20f, max(20.sp, 10.sp).value, 0f)
-        Assert.assertEquals(20f, max(20.sp, 20.sp).value, 0f)
-    }
-
-    @Test
-    fun coerceIn() {
-        Assert.assertEquals(10f, 10.sp.coerceIn(0.sp, 20.sp).value, 0f)
-        Assert.assertEquals(10f, 20.sp.coerceIn(0.sp, 10.sp).value, 0f)
-        Assert.assertEquals(10f, 0.sp.coerceIn(10.sp, 20.sp).value, 0f)
-        try {
-            10.sp.coerceIn(20.sp, 10.sp)
-            Assert.fail("Expected an exception here")
-        } catch (e: IllegalArgumentException) {
-            // success!
-        }
-    }
-
-    @Test
-    fun coerceAtLeast() {
-        Assert.assertEquals(10f, 0.sp.coerceAtLeast(10.sp).value, 0f)
-        Assert.assertEquals(10f, 10.sp.coerceAtLeast(5.sp).value, 0f)
-        Assert.assertEquals(10f, 10.sp.coerceAtLeast(10.sp).value, 0f)
-    }
-
-    @Test
-    fun coerceAtMost() {
-        Assert.assertEquals(10f, 100.sp.coerceAtMost(10.sp).value, 0f)
-        Assert.assertEquals(10f, 10.sp.coerceAtMost(20.sp).value, 0f)
-        Assert.assertEquals(10f, 10.sp.coerceAtMost(10.sp).value, 0f)
-    }
-
-    @Test
-    fun lerp() {
-        Assert.assertEquals(10f, lerp(0.sp, 10.sp, 1f).value, 0f)
-        Assert.assertEquals(0f, lerp(0.sp, 10.sp, 0f).value, 0f)
-        Assert.assertEquals(5f, lerp(0.sp, 10.sp, 0.5f).value, 0f)
-    }
-
-    @Test
-    fun inInherit() {
-        assertThat(Sp.Inherit.isInherit()).isTrue()
-        assertThat(Sp.Infinity.isInherit()).isFalse()
-        assertThat(1.sp.isInherit()).isFalse()
-    }
-
-    private fun <T> expectIllegalArgumentException(block: () -> T) {
-        try {
-            block()
-            Assert.fail("IllegalArgumentException is expected to be thrown")
-        } catch (e: IllegalArgumentException) {
-            // pass
-        }
-    }
-
-    @Test
-    fun inherit_plus_not_allowed() {
-        expectIllegalArgumentException { Sp.Inherit + 1.sp }
-        expectIllegalArgumentException { 1.sp + Sp.Inherit }
-
-        expectIllegalArgumentException { Sp.Inherit + Sp.Infinity }
-        expectIllegalArgumentException { Sp.Infinity + Sp.Inherit }
-    }
-
-    @Test
-    fun inherit_minus_not_allowed() {
-        expectIllegalArgumentException { Sp.Inherit - 1.sp }
-        expectIllegalArgumentException { 1.sp - Sp.Inherit }
-
-        expectIllegalArgumentException { Sp.Inherit - Sp.Infinity }
-        expectIllegalArgumentException { Sp.Infinity - Sp.Inherit }
-    }
-
-    @Test
-    fun inherit_unaryMinus_not_allowed() {
-        expectIllegalArgumentException { -Sp.Inherit }
-    }
-
-    @Test
-    fun inherit_div_not_allowed() {
-        expectIllegalArgumentException { Sp.Inherit / 1.0f }
-        // expectIllegalArgumentException { Sp.Inherit / 1.0 } Sp / Double is missing?
-        expectIllegalArgumentException { Sp.Inherit / 2 }
-        expectIllegalArgumentException { Sp.Inherit / 3.sp }
-        expectIllegalArgumentException { 3.sp / Sp.Inherit }
-        expectIllegalArgumentException { Sp.Inherit / Sp.Infinity }
-        expectIllegalArgumentException { Sp.Infinity / Sp.Inherit }
-    }
-
-    @Test
-    fun inherit_times_not_allowed() {
-        expectIllegalArgumentException { Sp.Inherit * 1.0f }
-        expectIllegalArgumentException { 1.0f * Sp.Inherit }
-        // expectIllegalArgumentException { Sp.Inherit * 1.0 }  Sp * Double is missing?
-        expectIllegalArgumentException { 1.0 * Sp.Inherit }
-        expectIllegalArgumentException { Sp.Inherit * 2 }
-        expectIllegalArgumentException { 2 * Sp.Inherit }
-    }
-
-    @Test
-    fun inherit_min_not_allowed() {
-        expectIllegalArgumentException { min(Sp.Inherit, 1.sp) }
-        expectIllegalArgumentException { min(1.sp, Sp.Inherit) }
-        expectIllegalArgumentException { min(Sp.Inherit, Sp.Inherit) }
-        expectIllegalArgumentException { min(Sp.Inherit, Sp.Infinity) }
-        expectIllegalArgumentException { min(Sp.Infinity, Sp.Inherit) }
-    }
-
-    @Test
-    fun inherit_max_not_allowed() {
-        expectIllegalArgumentException { max(Sp.Inherit, 1.sp) }
-        expectIllegalArgumentException { max(1.sp, Sp.Inherit) }
-        expectIllegalArgumentException { max(Sp.Inherit, Sp.Inherit) }
-        expectIllegalArgumentException { max(Sp.Inherit, Sp.Infinity) }
-        expectIllegalArgumentException { max(Sp.Infinity, Sp.Inherit) }
-    }
-
-    @Test
-    fun inherit_coerceIn_not_allowed() {
-        expectIllegalArgumentException { Sp.Inherit.coerceIn(1.sp, 2.sp) }
-        expectIllegalArgumentException { Sp.Inherit.coerceIn(Sp.Inherit, 2.sp) }
-        expectIllegalArgumentException { Sp.Inherit.coerceIn(1.sp, Sp.Inherit) }
-        expectIllegalArgumentException { Sp.Inherit.coerceIn(Sp.Inherit, Sp.Inherit) }
-        expectIllegalArgumentException { Sp.Inherit.coerceIn(Sp.Infinity, 2.sp) }
-        expectIllegalArgumentException { Sp.Inherit.coerceIn(Sp.Inherit, Sp.Infinity) }
-        expectIllegalArgumentException { Sp.Inherit.coerceIn(Sp.Infinity, Sp.Inherit) }
-        expectIllegalArgumentException { 1.sp.coerceIn(Sp.Inherit, 2.sp) }
-        expectIllegalArgumentException { 1.sp.coerceIn(1.sp, Sp.Inherit) }
-        expectIllegalArgumentException { 1.sp.coerceIn(Sp.Inherit, Sp.Inherit) }
-        expectIllegalArgumentException { 1.sp.coerceIn(Sp.Inherit, Sp.Infinity) }
-        expectIllegalArgumentException { 1.sp.coerceIn(Sp.Infinity, Sp.Inherit) }
-        expectIllegalArgumentException { Sp.Infinity.coerceIn(Sp.Inherit, 2.sp) }
-        expectIllegalArgumentException { Sp.Infinity.coerceIn(1.sp, Sp.Inherit) }
-        expectIllegalArgumentException { Sp.Infinity.coerceIn(Sp.Inherit, Sp.Inherit) }
-        expectIllegalArgumentException { Sp.Infinity.coerceIn(Sp.Inherit, Sp.Infinity) }
-        expectIllegalArgumentException { Sp.Infinity.coerceIn(Sp.Infinity, Sp.Inherit) }
-    }
-
-    @Test
-    fun inherit_coerceAtLeast_not_allowed() {
-        expectIllegalArgumentException { Sp.Inherit.coerceAtLeast(1.sp) }
-        expectIllegalArgumentException { Sp.Inherit.coerceAtLeast(Sp.Inherit) }
-        expectIllegalArgumentException { Sp.Inherit.coerceAtLeast(Sp.Infinity) }
-        expectIllegalArgumentException { 1.sp.coerceAtLeast(Sp.Inherit) }
-        expectIllegalArgumentException { Sp.Infinity.coerceAtLeast(Sp.Inherit) }
-    }
-
-    @Test
-    fun inherit_coerceAtMost_not_allowed() {
-        expectIllegalArgumentException { Sp.Inherit.coerceAtMost(1.sp) }
-        expectIllegalArgumentException { Sp.Inherit.coerceAtMost(Sp.Inherit) }
-        expectIllegalArgumentException { Sp.Inherit.coerceAtMost(Sp.Infinity) }
-        expectIllegalArgumentException { 1.sp.coerceAtMost(Sp.Inherit) }
-        expectIllegalArgumentException { Sp.Infinity.coerceAtMost(Sp.Inherit) }
-    }
-
-    @Test
-    fun inherit_lerp_not_allowed() {
-        expectIllegalArgumentException { lerp(Sp.Inherit, 1.sp, 0.3f) }
-        expectIllegalArgumentException { lerp(1.sp, Sp.Inherit, 0.3f) }
-        expectIllegalArgumentException { lerp(Sp.Inherit, Sp.Inherit, 0.3f) }
-        expectIllegalArgumentException { lerp(Sp.Inherit, Sp.Infinity, 0.3f) }
-        expectIllegalArgumentException { lerp(Sp.Infinity, Sp.Inherit, 0.3f) }
-    }
-}
\ No newline at end of file
diff --git a/ui/ui-core/src/test/java/androidx/ui/core/TextUnitTest.kt b/ui/ui-core/src/test/java/androidx/ui/core/TextUnitTest.kt
new file mode 100644
index 0000000..b138a34
--- /dev/null
+++ b/ui/ui-core/src/test/java/androidx/ui/core/TextUnitTest.kt
@@ -0,0 +1,1049 @@
+/*
+ * 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.ui.core
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.lang.RuntimeException
+
+@RunWith(JUnit4::class)
+class TextUnitTest {
+    @Test
+    fun construct_sp_from_float() {
+        TextUnit.Sp(5f).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun construct_sp_from_int() {
+        TextUnit.Sp(5).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun construct_sp_from_double() {
+        TextUnit.Sp(5.0).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun construct_sp_from_float_extension() {
+        5f.sp.also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun construct_sp_from_int_extension() {
+        5.sp.also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun construct_sp_from_double_extension() {
+        5.0.sp.also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun construct_em_from_float() {
+        TextUnit.Em(5f).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun construct_em_from_int() {
+        TextUnit.Em(5).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun construct_em_from_double() {
+        TextUnit.Em(5.0).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun construct_em_from_float_extension() {
+        5f.em.also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun construct_em_from_int_extension() {
+        5.em.also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun construct_em_from_double_extension() {
+        5.0.em.also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(5f)
+        }
+    }
+
+    @Test
+    fun inherit_type_check() {
+        TextUnit.Inherit.also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isTrue()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Inherit)
+        }
+    }
+
+    // Additions
+    @Test
+    fun add_sp_sp() {
+        (1.sp + 2.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(3f)
+        }
+    }
+
+    @Test
+    fun add_em_em() {
+        (1.em + 2.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(3f)
+        }
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun add_sp_em() {
+        1.sp + 2.em
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun add_sp_inherit() {
+        1.sp + TextUnit.Inherit
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun add_em_sp() {
+        1.em + 2.sp
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun add_em_inherit() {
+        1.em + TextUnit.Inherit
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun add_inherit_inherit() {
+        TextUnit.Inherit + TextUnit.Inherit
+    }
+
+    // Subtractions
+    @Test
+    fun sub_sp_sp() {
+        (1.sp - 2.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(-1f)
+        }
+    }
+
+    @Test
+    fun sub_em_em() {
+        (1.em - 2.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(-1f)
+        }
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun sub_sp_em() {
+        1.sp - 2.em
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun sub_sp_inherit() {
+        1.sp - TextUnit.Inherit
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun sub_em_sp() {
+        1.em - 2.sp
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun sub_em_inherit() {
+        1.em - TextUnit.Inherit
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun sub_inherit_inherit() {
+        TextUnit.Inherit - TextUnit.Inherit
+    }
+
+    // Unary minuses
+    @Test
+    fun minus_em() {
+        -(1.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(1f)
+        }
+    }
+
+    @Test
+    fun minus_sp() {
+        -(1.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(1f)
+        }
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun minus_inherit() {
+        -TextUnit.Inherit
+    }
+
+    // Multiplications
+    @Test
+    fun multiply_sp_float() {
+        (2.sp * 3f).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test
+    fun multiply_sp_double() {
+        (2.sp * 3.0).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test
+    fun multiply_sp_int() {
+        (2.sp * 3).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test
+    fun multiply_float_sp() {
+        (2f * 3.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test
+    fun multiply_double_sp() {
+        (2.0 * 3.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test
+    fun multiply_int_sp() {
+        (2 * 3.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test
+    fun multiply_float_em() {
+        (2f * 3.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test
+    fun multiply_double_em() {
+        (2.0 * 3.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test
+    fun multiply_int_em() {
+        (2 * 3.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test
+    fun multiply_em_float() {
+        (2.em * 3f).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test
+    fun multiply_em_double() {
+        (2.em * 3.0).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test
+    fun multiply_em_int() {
+        (2.em * 3).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(6f)
+        }
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun multiply_inherit_float() {
+        TextUnit.Inherit * 3f
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun multiply_inherit_double() {
+        TextUnit.Inherit * 3.0
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun multiply_inherit_int() {
+        TextUnit.Inherit * 3
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun multiply_float_inherit() {
+        3f * TextUnit.Inherit
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun multiply_double_inherit() {
+        3.0f * TextUnit.Inherit
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun multiply_int_inherit() {
+        3 * TextUnit.Inherit
+    }
+
+    // Divisions
+    @Test
+    fun divide_sp_float() {
+        (1.sp / 2f).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(0.5f)
+        }
+    }
+
+    @Test
+    fun divide_sp_double() {
+        (1.sp / 2.0).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(0.5f)
+        }
+    }
+
+    @Test
+    fun divide_sp_int() {
+        (1.sp / 2).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(0.5f)
+        }
+    }
+
+    @Test
+    fun divide_em_float() {
+        (1.em / 2f).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(0.5f)
+        }
+    }
+
+    @Test
+    fun divide_em_double() {
+        (1.em / 2.0).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(0.5f)
+        }
+    }
+
+    @Test
+    fun divide_em_int() {
+        (1.em / 2).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(0.5f)
+        }
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun divide_inherit_float() {
+        TextUnit.Inherit / 2f
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun divide_inherit_double() {
+        TextUnit.Inherit / 2.0
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun divide_inherit_int() {
+        TextUnit.Inherit / 2
+    }
+
+    @Test
+    fun divide_sp_sp() {
+        assertThat(1.sp / 2.sp).isEqualTo(0.5f)
+    }
+
+    @Test
+    fun divide_em_em() {
+        assertThat(1.em / 2.em).isEqualTo(0.5f)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun divide_sp_em() {
+        1.sp / 2.em
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun divide_em_sp() {
+        1.em / 2.sp
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun divide_inherit_sp() {
+        TextUnit.Inherit / 2.sp
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun divide_inherit_em() {
+        TextUnit.Inherit / 2.em
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun divide_sp_inherit() {
+        1.sp / TextUnit.Inherit
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun divide_em_inherit() {
+        1.em / TextUnit.Inherit
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun divide_inherit_inherit() {
+        TextUnit.Inherit / TextUnit.Inherit
+    }
+
+    // Comparisons
+    @Test
+    fun compare_sp_sp() {
+        assertThat(1.sp > 2.sp).isFalse()
+        assertThat(1.sp >= 2.sp).isFalse()
+        assertThat(1.sp < 2.sp).isTrue()
+        assertThat(1.sp <= 2.sp).isTrue()
+        assertThat(1.sp == 2.sp).isFalse()
+
+        assertThat(2.sp > 1.sp).isTrue()
+        assertThat(2.sp >= 1.sp).isTrue()
+        assertThat(2.sp < 1.sp).isFalse()
+        assertThat(2.sp <= 1.sp).isFalse()
+        assertThat(2.sp == 1.sp).isFalse()
+
+        assertThat(2.sp > 2.sp).isFalse()
+        assertThat(2.sp >= 2.sp).isTrue()
+        assertThat(2.sp < 2.sp).isFalse()
+        assertThat(2.sp <= 2.sp).isTrue()
+        assertThat(2.sp == 2.sp).isTrue()
+    }
+
+    @Test
+    fun compare_em_em() {
+        assertThat(1.em > 2.em).isFalse()
+        assertThat(1.em >= 2.em).isFalse()
+        assertThat(1.em < 2.em).isTrue()
+        assertThat(1.em <= 2.em).isTrue()
+        assertThat(1.em == 2.em).isFalse()
+
+        assertThat(2.em > 1.em).isTrue()
+        assertThat(2.em >= 1.em).isTrue()
+        assertThat(2.em < 1.em).isFalse()
+        assertThat(2.em <= 1.em).isFalse()
+        assertThat(2.em == 1.em).isFalse()
+
+        assertThat(2.em > 2.em).isFalse()
+        assertThat(2.em >= 2.em).isTrue()
+        assertThat(2.em < 2.em).isFalse()
+        assertThat(2.em <= 2.em).isTrue()
+        assertThat(2.em == 2.em).isTrue()
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun compare_sp_em() {
+        1.sp > 2.em
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun compare_em_sp() {
+        1.em > 2.sp
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun compare_inherit_sp() {
+        TextUnit.Inherit > 2.sp
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun compare_sp_inherit() {
+        1.sp > TextUnit.Inherit
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun compare_inherit_em() {
+        TextUnit.Inherit > 2.em
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun compare_em_inherit() {
+        1.em > TextUnit.Inherit
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun compare_inherit_inherit() {
+        TextUnit.Inherit > TextUnit.Inherit
+    }
+
+    // Equalities
+    @Test
+    fun equals_sp_sp() {
+        assertThat(2.sp == 2.0.sp).isTrue()
+        assertThat(2.sp == 1.0.sp).isFalse()
+    }
+
+    @Test
+    fun equals_em_em() {
+        assertThat(2.em == 2.0.em).isTrue()
+        assertThat(2.em == 1.0.em).isFalse()
+    }
+
+    @Test
+    fun equals_inherit_inherit() {
+        assertThat(TextUnit.Inherit == TextUnit.Inherit).isTrue()
+    }
+
+    @Test
+    fun equals_sp_em() {
+        assertThat(2.sp == 2.0.em).isFalse()
+        assertThat(2.sp == 1.0.em).isFalse()
+    }
+
+    @Test
+    fun equals_em_sp() {
+        assertThat(2.em == 2.0.sp).isFalse()
+        assertThat(2.em == 1.0.sp).isFalse()
+    }
+
+    @Test
+    fun equals_sp_inherit() {
+        assertThat(2.sp == TextUnit.Inherit).isFalse()
+    }
+
+    @Test
+    fun equals_inherit_sp() {
+        assertThat(TextUnit.Inherit == 2.sp).isFalse()
+    }
+
+    @Test
+    fun equals_em_inherit() {
+        assertThat(2.em == TextUnit.Inherit).isFalse()
+    }
+
+    @Test
+    fun equals_inherit_em() {
+        assertThat(TextUnit.Inherit == 2.em).isFalse()
+    }
+
+    // Mins
+    @Test
+    fun min_sp_sp() {
+        min(1.sp, 2.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(1f)
+        }
+    }
+
+    @Test
+    fun min_em_em() {
+        min(1.em, 2.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(1f)
+        }
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun min_sp_em() {
+        min(1.sp, 2.em)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun min_sp_inherit() {
+        min(1.sp, TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun min_em_sp() {
+        min(1.em, 2.sp)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun min_em_inherit() {
+        min(1.em, TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun min_inherit_inherit() {
+        min(TextUnit.Inherit, TextUnit.Inherit)
+    }
+
+    // Maxes
+    @Test
+    fun max_sp_sp() {
+        max(1.sp, 2.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(2f)
+        }
+    }
+
+    @Test
+    fun max_em_em() {
+        max(1.em, 2.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(2f)
+        }
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun max_sp_em() {
+        max(1.sp, 2.em)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun max_sp_inherit() {
+        max(1.sp, TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun max_em_sp() {
+        max(1.em, 2.sp)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun max_em_inherit() {
+        max(1.em, TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun max_inherit_inherit() {
+        max(TextUnit.Inherit, TextUnit.Inherit)
+    }
+
+    // coerceIns
+    @Test
+    fun coerceIn_sp_sp_sp() {
+        3.sp.coerceIn(1.sp, 2.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(2f)
+        }
+
+        0.sp.coerceIn(1.sp, 2.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(1f)
+        }
+    }
+
+    @Test
+    fun coerceIn_em_em_em() {
+        3.em.coerceIn(1.em, 2.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(2f)
+        }
+
+        0.em.coerceIn(1.em, 2.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(1f)
+        }
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_sp_sp_em() {
+        1.sp.coerceIn(1.sp, 1.em)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_sp_sp_inherit() {
+        1.sp.coerceIn(1.sp, TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_sp_em_sp() {
+        1.sp.coerceIn(1.em, 1.sp)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_sp_em_em() {
+        1.sp.coerceIn(1.em, 1.em)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_sp_em_inherit() {
+        1.sp.coerceIn(1.em, TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_em_sp_sp() {
+        1.em.coerceIn(1.sp, 1.sp)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_em_sp_em() {
+        1.em.coerceIn(1.sp, 1.em)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_em_sp_inherit() {
+        1.em.coerceIn(1.sp, TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_em_em_sp() {
+        1.em.coerceIn(1.em, 1.sp)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_em_em_inherit() {
+        1.em.coerceIn(1.em, TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_inherit_sp_sp() {
+        TextUnit.Inherit.coerceIn(1.sp, 1.sp)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_inherit_sp_em() {
+        TextUnit.Inherit.coerceIn(1.sp, 1.em)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_inherit_sp_inherit() {
+        TextUnit.Inherit.coerceIn(1.sp, TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_inherit_em_sp() {
+        TextUnit.Inherit.coerceIn(1.em, 1.sp)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_inherit_em_em() {
+        TextUnit.Inherit.coerceIn(1.em, 1.em)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceIn_inherit_em_inherit() {
+        TextUnit.Inherit.coerceIn(TextUnit.Inherit, TextUnit.Inherit)
+    }
+
+    // coerceAtLeasts
+    @Test
+    fun coerceAtLeast_sp_sp() {
+        1.sp.coerceAtLeast(2.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(2f)
+        }
+        3.sp.coerceAtLeast(2.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(3f)
+        }
+    }
+
+    @Test
+    fun coerceAtLeast_em_em() {
+        1.em.coerceAtLeast(2.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(2f)
+        }
+        3.em.coerceAtLeast(2.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(3f)
+        }
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceAtLeast_sp_em() {
+        1.sp.coerceAtLeast(2.em)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceAtLeast_sp_inherit() {
+        1.sp.coerceAtLeast(TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceAtLeast_em_sp() {
+        1.em.coerceAtLeast(1.sp)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceAtLeast_em_inherit() {
+        1.em.coerceAtLeast(TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceAtLeast_inherit_inherit() {
+        TextUnit.Inherit.coerceAtLeast(TextUnit.Inherit)
+    }
+
+    // coerceAtMosts
+    @Test
+    fun coerceAtMost_sp_sp() {
+        1.sp.coerceAtMost(2.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(1f)
+        }
+        3.sp.coerceAtMost(2.sp).also {
+            assertThat(it.isSp).isTrue()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isFalse()
+            assertThat(it.type).isEqualTo(TextUnitType.Sp)
+            assertThat(it.value).isEqualTo(2f)
+        }
+    }
+
+    @Test
+    fun coerceAtMost_em_em() {
+        1.em.coerceAtMost(2.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(1f)
+        }
+        3.em.coerceAtMost(2.em).also {
+            assertThat(it.isSp).isFalse()
+            assertThat(it.isInherit).isFalse()
+            assertThat(it.isEm).isTrue()
+            assertThat(it.type).isEqualTo(TextUnitType.Em)
+            assertThat(it.value).isEqualTo(2f)
+        }
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceAtMost_sp_em() {
+        1.sp.coerceAtMost(2.em)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceAtMost_sp_inherit() {
+        1.sp.coerceAtMost(TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceAtMost_em_sp() {
+        1.em.coerceAtMost(1.sp)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceAtMost_em_inherit() {
+        1.em.coerceAtMost(TextUnit.Inherit)
+    }
+
+    @Test(expected = RuntimeException::class)
+    fun coerceAtMost_inherit_inherit() {
+        TextUnit.Inherit.coerceAtMost(TextUnit.Inherit)
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/selection/TextSelectionDelegateTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/selection/TextSelectionDelegateTest.kt
index c7e54bf..1786c18 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/selection/TextSelectionDelegateTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/selection/TextSelectionDelegateTest.kt
@@ -25,7 +25,7 @@
 import androidx.ui.core.Density
 import androidx.ui.core.LayoutDirection
 import androidx.ui.core.PxPosition
-import androidx.ui.core.Sp
+import androidx.ui.core.TextUnit
 import androidx.ui.core.px
 import androidx.ui.core.sp
 import androidx.ui.core.withDensity
@@ -825,7 +825,7 @@
 
     private fun simpleTextDelegate(
         text: String = "",
-        fontSize: Sp = Sp.Inherit,
+        fontSize: TextUnit = TextUnit.Inherit,
         density: Density
     ): TextDelegate {
         val textStyle = TextStyle(fontSize = fontSize, fontFamily = fontFamily)
diff --git a/ui/ui-text/api/0.1.0-dev03.txt b/ui/ui-text/api/0.1.0-dev03.txt
index eaa847e..36ba5b6 100644
--- a/ui/ui-text/api/0.1.0-dev03.txt
+++ b/ui/ui-text/api/0.1.0-dev03.txt
@@ -395,14 +395,14 @@
   }
 
   public final class ParagraphStyle {
-    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
     ctor public ParagraphStyle();
     method public androidx.ui.text.style.TextAlign? component1();
     method public androidx.ui.text.style.TextDirectionAlgorithm? component2();
-    method public androidx.ui.core.Sp component3();
+    method public androidx.ui.core.TextUnit component3();
     method public androidx.ui.text.style.TextIndent? component4();
-    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
-    method public androidx.ui.core.Sp getLineHeight();
+    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    method public androidx.ui.core.TextUnit getLineHeight();
     method public androidx.ui.text.style.TextAlign? getTextAlign();
     method public androidx.ui.text.style.TextDirectionAlgorithm? getTextDirectionAlgorithm();
     method public androidx.ui.text.style.TextIndent? getTextIndent();
@@ -464,36 +464,34 @@
   }
 
   public final class TextStyle {
-    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     ctor public TextStyle();
     method public androidx.ui.graphics.Color? component1();
-    method public androidx.ui.text.style.BaselineShift? component10();
-    method public androidx.ui.text.style.TextGeometricTransform? component11();
-    method public androidx.ui.text.LocaleList? component12();
-    method public androidx.ui.graphics.Color? component13();
-    method public androidx.ui.text.style.TextDecoration? component14();
-    method public androidx.ui.graphics.Shadow? component15();
-    method public androidx.ui.core.Sp component2();
-    method public Float? component3();
-    method public androidx.ui.text.font.FontWeight? component4();
-    method public androidx.ui.text.font.FontStyle? component5();
-    method public androidx.ui.text.font.FontSynthesis? component6();
-    method public androidx.ui.text.font.FontFamily? component7();
-    method public String? component8();
-    method public androidx.ui.core.Em? component9();
-    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    method public androidx.ui.text.style.TextGeometricTransform? component10();
+    method public androidx.ui.text.LocaleList? component11();
+    method public androidx.ui.graphics.Color? component12();
+    method public androidx.ui.text.style.TextDecoration? component13();
+    method public androidx.ui.graphics.Shadow? component14();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.font.FontWeight? component3();
+    method public androidx.ui.text.font.FontStyle? component4();
+    method public androidx.ui.text.font.FontSynthesis? component5();
+    method public androidx.ui.text.font.FontFamily? component6();
+    method public String? component7();
+    method public androidx.ui.core.TextUnit component8();
+    method public androidx.ui.text.style.BaselineShift? component9();
+    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     method public androidx.ui.graphics.Color? getBackground();
     method public androidx.ui.text.style.BaselineShift? getBaselineShift();
     method public androidx.ui.graphics.Color? getColor();
     method public androidx.ui.text.style.TextDecoration? getDecoration();
     method public androidx.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
-    method public androidx.ui.core.Sp getFontSize();
-    method public Float? getFontSizeScale();
+    method public androidx.ui.core.TextUnit getFontSize();
     method public androidx.ui.text.font.FontStyle? getFontStyle();
     method public androidx.ui.text.font.FontSynthesis? getFontSynthesis();
     method public androidx.ui.text.font.FontWeight? getFontWeight();
-    method public androidx.ui.core.Em? getLetterSpacing();
+    method public androidx.ui.core.TextUnit getLetterSpacing();
     method public androidx.ui.text.LocaleList? getLocaleList();
     method public androidx.ui.graphics.Shadow? getShadow();
     method public androidx.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
@@ -711,13 +709,13 @@
   }
 
   public final class TextIndent {
-    ctor public TextIndent(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
+    ctor public TextIndent(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
     ctor public TextIndent();
-    method public androidx.ui.core.Sp component1();
-    method public androidx.ui.core.Sp component2();
-    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
-    method public androidx.ui.core.Sp getFirstLine();
-    method public androidx.ui.core.Sp getRestLine();
+    method public androidx.ui.core.TextUnit component1();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
+    method public androidx.ui.core.TextUnit getFirstLine();
+    method public androidx.ui.core.TextUnit getRestLine();
   }
 
   public final class TextIndentKt {
diff --git a/ui/ui-text/api/api_lint.ignore b/ui/ui-text/api/api_lint.ignore
index 95e7f45..5e5dc9c 100644
--- a/ui/ui-text/api/api_lint.ignore
+++ b/ui/ui-text/api/api_lint.ignore
@@ -9,14 +9,6 @@
     Must avoid boxed primitives (`java.lang.Integer`)
 AutoBoxing: androidx.ui.text.ParagraphKt#Paragraph(androidx.ui.text.ParagraphIntrinsics, Integer, Boolean, androidx.ui.text.ParagraphConstraints) parameter #1:
     Must avoid boxed primitives (`java.lang.Integer`)
-AutoBoxing: androidx.ui.text.TextStyle#TextStyle(androidx.ui.graphics.Color, androidx.ui.core.Sp, Float, androidx.ui.text.font.FontWeight, androidx.ui.text.font.FontStyle, androidx.ui.text.font.FontSynthesis, androidx.ui.text.font.FontFamily, String, androidx.ui.core.Em, androidx.ui.text.style.BaselineShift, androidx.ui.text.style.TextGeometricTransform, androidx.ui.text.LocaleList, androidx.ui.graphics.Color, androidx.ui.text.style.TextDecoration, androidx.ui.graphics.Shadow) parameter #2:
-    Must avoid boxed primitives (`java.lang.Float`)
-AutoBoxing: androidx.ui.text.TextStyle#component3():
-    Must avoid boxed primitives (`java.lang.Float`)
-AutoBoxing: androidx.ui.text.TextStyle#copy(androidx.ui.graphics.Color, androidx.ui.core.Sp, Float, androidx.ui.text.font.FontWeight, androidx.ui.text.font.FontStyle, androidx.ui.text.font.FontSynthesis, androidx.ui.text.font.FontFamily, String, androidx.ui.core.Em, androidx.ui.text.style.BaselineShift, androidx.ui.text.style.TextGeometricTransform, androidx.ui.text.LocaleList, androidx.ui.graphics.Color, androidx.ui.text.style.TextDecoration, androidx.ui.graphics.Shadow) parameter #2:
-    Must avoid boxed primitives (`java.lang.Float`)
-AutoBoxing: androidx.ui.text.TextStyle#getFontSizeScale():
-    Must avoid boxed primitives (`java.lang.Float`)
 AutoBoxing: androidx.ui.text.style.TextGeometricTransform#TextGeometricTransform(Float, Float) parameter #0:
     Must avoid boxed primitives (`java.lang.Float`)
 AutoBoxing: androidx.ui.text.style.TextGeometricTransform#TextGeometricTransform(Float, Float) parameter #1:
diff --git a/ui/ui-text/api/current.txt b/ui/ui-text/api/current.txt
index eaa847e..36ba5b6 100644
--- a/ui/ui-text/api/current.txt
+++ b/ui/ui-text/api/current.txt
@@ -395,14 +395,14 @@
   }
 
   public final class ParagraphStyle {
-    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
     ctor public ParagraphStyle();
     method public androidx.ui.text.style.TextAlign? component1();
     method public androidx.ui.text.style.TextDirectionAlgorithm? component2();
-    method public androidx.ui.core.Sp component3();
+    method public androidx.ui.core.TextUnit component3();
     method public androidx.ui.text.style.TextIndent? component4();
-    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
-    method public androidx.ui.core.Sp getLineHeight();
+    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    method public androidx.ui.core.TextUnit getLineHeight();
     method public androidx.ui.text.style.TextAlign? getTextAlign();
     method public androidx.ui.text.style.TextDirectionAlgorithm? getTextDirectionAlgorithm();
     method public androidx.ui.text.style.TextIndent? getTextIndent();
@@ -464,36 +464,34 @@
   }
 
   public final class TextStyle {
-    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     ctor public TextStyle();
     method public androidx.ui.graphics.Color? component1();
-    method public androidx.ui.text.style.BaselineShift? component10();
-    method public androidx.ui.text.style.TextGeometricTransform? component11();
-    method public androidx.ui.text.LocaleList? component12();
-    method public androidx.ui.graphics.Color? component13();
-    method public androidx.ui.text.style.TextDecoration? component14();
-    method public androidx.ui.graphics.Shadow? component15();
-    method public androidx.ui.core.Sp component2();
-    method public Float? component3();
-    method public androidx.ui.text.font.FontWeight? component4();
-    method public androidx.ui.text.font.FontStyle? component5();
-    method public androidx.ui.text.font.FontSynthesis? component6();
-    method public androidx.ui.text.font.FontFamily? component7();
-    method public String? component8();
-    method public androidx.ui.core.Em? component9();
-    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    method public androidx.ui.text.style.TextGeometricTransform? component10();
+    method public androidx.ui.text.LocaleList? component11();
+    method public androidx.ui.graphics.Color? component12();
+    method public androidx.ui.text.style.TextDecoration? component13();
+    method public androidx.ui.graphics.Shadow? component14();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.font.FontWeight? component3();
+    method public androidx.ui.text.font.FontStyle? component4();
+    method public androidx.ui.text.font.FontSynthesis? component5();
+    method public androidx.ui.text.font.FontFamily? component6();
+    method public String? component7();
+    method public androidx.ui.core.TextUnit component8();
+    method public androidx.ui.text.style.BaselineShift? component9();
+    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     method public androidx.ui.graphics.Color? getBackground();
     method public androidx.ui.text.style.BaselineShift? getBaselineShift();
     method public androidx.ui.graphics.Color? getColor();
     method public androidx.ui.text.style.TextDecoration? getDecoration();
     method public androidx.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
-    method public androidx.ui.core.Sp getFontSize();
-    method public Float? getFontSizeScale();
+    method public androidx.ui.core.TextUnit getFontSize();
     method public androidx.ui.text.font.FontStyle? getFontStyle();
     method public androidx.ui.text.font.FontSynthesis? getFontSynthesis();
     method public androidx.ui.text.font.FontWeight? getFontWeight();
-    method public androidx.ui.core.Em? getLetterSpacing();
+    method public androidx.ui.core.TextUnit getLetterSpacing();
     method public androidx.ui.text.LocaleList? getLocaleList();
     method public androidx.ui.graphics.Shadow? getShadow();
     method public androidx.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
@@ -711,13 +709,13 @@
   }
 
   public final class TextIndent {
-    ctor public TextIndent(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
+    ctor public TextIndent(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
     ctor public TextIndent();
-    method public androidx.ui.core.Sp component1();
-    method public androidx.ui.core.Sp component2();
-    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
-    method public androidx.ui.core.Sp getFirstLine();
-    method public androidx.ui.core.Sp getRestLine();
+    method public androidx.ui.core.TextUnit component1();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
+    method public androidx.ui.core.TextUnit getFirstLine();
+    method public androidx.ui.core.TextUnit getRestLine();
   }
 
   public final class TextIndentKt {
diff --git a/ui/ui-text/api/public_plus_experimental_0.1.0-dev03.txt b/ui/ui-text/api/public_plus_experimental_0.1.0-dev03.txt
index eaa847e..36ba5b6 100644
--- a/ui/ui-text/api/public_plus_experimental_0.1.0-dev03.txt
+++ b/ui/ui-text/api/public_plus_experimental_0.1.0-dev03.txt
@@ -395,14 +395,14 @@
   }
 
   public final class ParagraphStyle {
-    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
     ctor public ParagraphStyle();
     method public androidx.ui.text.style.TextAlign? component1();
     method public androidx.ui.text.style.TextDirectionAlgorithm? component2();
-    method public androidx.ui.core.Sp component3();
+    method public androidx.ui.core.TextUnit component3();
     method public androidx.ui.text.style.TextIndent? component4();
-    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
-    method public androidx.ui.core.Sp getLineHeight();
+    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    method public androidx.ui.core.TextUnit getLineHeight();
     method public androidx.ui.text.style.TextAlign? getTextAlign();
     method public androidx.ui.text.style.TextDirectionAlgorithm? getTextDirectionAlgorithm();
     method public androidx.ui.text.style.TextIndent? getTextIndent();
@@ -464,36 +464,34 @@
   }
 
   public final class TextStyle {
-    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     ctor public TextStyle();
     method public androidx.ui.graphics.Color? component1();
-    method public androidx.ui.text.style.BaselineShift? component10();
-    method public androidx.ui.text.style.TextGeometricTransform? component11();
-    method public androidx.ui.text.LocaleList? component12();
-    method public androidx.ui.graphics.Color? component13();
-    method public androidx.ui.text.style.TextDecoration? component14();
-    method public androidx.ui.graphics.Shadow? component15();
-    method public androidx.ui.core.Sp component2();
-    method public Float? component3();
-    method public androidx.ui.text.font.FontWeight? component4();
-    method public androidx.ui.text.font.FontStyle? component5();
-    method public androidx.ui.text.font.FontSynthesis? component6();
-    method public androidx.ui.text.font.FontFamily? component7();
-    method public String? component8();
-    method public androidx.ui.core.Em? component9();
-    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    method public androidx.ui.text.style.TextGeometricTransform? component10();
+    method public androidx.ui.text.LocaleList? component11();
+    method public androidx.ui.graphics.Color? component12();
+    method public androidx.ui.text.style.TextDecoration? component13();
+    method public androidx.ui.graphics.Shadow? component14();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.font.FontWeight? component3();
+    method public androidx.ui.text.font.FontStyle? component4();
+    method public androidx.ui.text.font.FontSynthesis? component5();
+    method public androidx.ui.text.font.FontFamily? component6();
+    method public String? component7();
+    method public androidx.ui.core.TextUnit component8();
+    method public androidx.ui.text.style.BaselineShift? component9();
+    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     method public androidx.ui.graphics.Color? getBackground();
     method public androidx.ui.text.style.BaselineShift? getBaselineShift();
     method public androidx.ui.graphics.Color? getColor();
     method public androidx.ui.text.style.TextDecoration? getDecoration();
     method public androidx.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
-    method public androidx.ui.core.Sp getFontSize();
-    method public Float? getFontSizeScale();
+    method public androidx.ui.core.TextUnit getFontSize();
     method public androidx.ui.text.font.FontStyle? getFontStyle();
     method public androidx.ui.text.font.FontSynthesis? getFontSynthesis();
     method public androidx.ui.text.font.FontWeight? getFontWeight();
-    method public androidx.ui.core.Em? getLetterSpacing();
+    method public androidx.ui.core.TextUnit getLetterSpacing();
     method public androidx.ui.text.LocaleList? getLocaleList();
     method public androidx.ui.graphics.Shadow? getShadow();
     method public androidx.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
@@ -711,13 +709,13 @@
   }
 
   public final class TextIndent {
-    ctor public TextIndent(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
+    ctor public TextIndent(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
     ctor public TextIndent();
-    method public androidx.ui.core.Sp component1();
-    method public androidx.ui.core.Sp component2();
-    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
-    method public androidx.ui.core.Sp getFirstLine();
-    method public androidx.ui.core.Sp getRestLine();
+    method public androidx.ui.core.TextUnit component1();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
+    method public androidx.ui.core.TextUnit getFirstLine();
+    method public androidx.ui.core.TextUnit getRestLine();
   }
 
   public final class TextIndentKt {
diff --git a/ui/ui-text/api/public_plus_experimental_current.txt b/ui/ui-text/api/public_plus_experimental_current.txt
index eaa847e..36ba5b6 100644
--- a/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/ui/ui-text/api/public_plus_experimental_current.txt
@@ -395,14 +395,14 @@
   }
 
   public final class ParagraphStyle {
-    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
     ctor public ParagraphStyle();
     method public androidx.ui.text.style.TextAlign? component1();
     method public androidx.ui.text.style.TextDirectionAlgorithm? component2();
-    method public androidx.ui.core.Sp component3();
+    method public androidx.ui.core.TextUnit component3();
     method public androidx.ui.text.style.TextIndent? component4();
-    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
-    method public androidx.ui.core.Sp getLineHeight();
+    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    method public androidx.ui.core.TextUnit getLineHeight();
     method public androidx.ui.text.style.TextAlign? getTextAlign();
     method public androidx.ui.text.style.TextDirectionAlgorithm? getTextDirectionAlgorithm();
     method public androidx.ui.text.style.TextIndent? getTextIndent();
@@ -464,36 +464,34 @@
   }
 
   public final class TextStyle {
-    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     ctor public TextStyle();
     method public androidx.ui.graphics.Color? component1();
-    method public androidx.ui.text.style.BaselineShift? component10();
-    method public androidx.ui.text.style.TextGeometricTransform? component11();
-    method public androidx.ui.text.LocaleList? component12();
-    method public androidx.ui.graphics.Color? component13();
-    method public androidx.ui.text.style.TextDecoration? component14();
-    method public androidx.ui.graphics.Shadow? component15();
-    method public androidx.ui.core.Sp component2();
-    method public Float? component3();
-    method public androidx.ui.text.font.FontWeight? component4();
-    method public androidx.ui.text.font.FontStyle? component5();
-    method public androidx.ui.text.font.FontSynthesis? component6();
-    method public androidx.ui.text.font.FontFamily? component7();
-    method public String? component8();
-    method public androidx.ui.core.Em? component9();
-    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    method public androidx.ui.text.style.TextGeometricTransform? component10();
+    method public androidx.ui.text.LocaleList? component11();
+    method public androidx.ui.graphics.Color? component12();
+    method public androidx.ui.text.style.TextDecoration? component13();
+    method public androidx.ui.graphics.Shadow? component14();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.font.FontWeight? component3();
+    method public androidx.ui.text.font.FontStyle? component4();
+    method public androidx.ui.text.font.FontSynthesis? component5();
+    method public androidx.ui.text.font.FontFamily? component6();
+    method public String? component7();
+    method public androidx.ui.core.TextUnit component8();
+    method public androidx.ui.text.style.BaselineShift? component9();
+    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     method public androidx.ui.graphics.Color? getBackground();
     method public androidx.ui.text.style.BaselineShift? getBaselineShift();
     method public androidx.ui.graphics.Color? getColor();
     method public androidx.ui.text.style.TextDecoration? getDecoration();
     method public androidx.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
-    method public androidx.ui.core.Sp getFontSize();
-    method public Float? getFontSizeScale();
+    method public androidx.ui.core.TextUnit getFontSize();
     method public androidx.ui.text.font.FontStyle? getFontStyle();
     method public androidx.ui.text.font.FontSynthesis? getFontSynthesis();
     method public androidx.ui.text.font.FontWeight? getFontWeight();
-    method public androidx.ui.core.Em? getLetterSpacing();
+    method public androidx.ui.core.TextUnit getLetterSpacing();
     method public androidx.ui.text.LocaleList? getLocaleList();
     method public androidx.ui.graphics.Shadow? getShadow();
     method public androidx.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
@@ -711,13 +709,13 @@
   }
 
   public final class TextIndent {
-    ctor public TextIndent(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
+    ctor public TextIndent(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
     ctor public TextIndent();
-    method public androidx.ui.core.Sp component1();
-    method public androidx.ui.core.Sp component2();
-    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
-    method public androidx.ui.core.Sp getFirstLine();
-    method public androidx.ui.core.Sp getRestLine();
+    method public androidx.ui.core.TextUnit component1();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
+    method public androidx.ui.core.TextUnit getFirstLine();
+    method public androidx.ui.core.TextUnit getRestLine();
   }
 
   public final class TextIndentKt {
diff --git a/ui/ui-text/api/restricted_0.1.0-dev03.txt b/ui/ui-text/api/restricted_0.1.0-dev03.txt
index 5157e30..8e81f5f 100644
--- a/ui/ui-text/api/restricted_0.1.0-dev03.txt
+++ b/ui/ui-text/api/restricted_0.1.0-dev03.txt
@@ -397,14 +397,14 @@
   }
 
   public final class ParagraphStyle {
-    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
     ctor public ParagraphStyle();
     method public androidx.ui.text.style.TextAlign? component1();
     method public androidx.ui.text.style.TextDirectionAlgorithm? component2();
-    method public androidx.ui.core.Sp component3();
+    method public androidx.ui.core.TextUnit component3();
     method public androidx.ui.text.style.TextIndent? component4();
-    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
-    method public androidx.ui.core.Sp getLineHeight();
+    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    method public androidx.ui.core.TextUnit getLineHeight();
     method public androidx.ui.text.style.TextAlign? getTextAlign();
     method public androidx.ui.text.style.TextDirectionAlgorithm? getTextDirectionAlgorithm();
     method public androidx.ui.text.style.TextIndent? getTextIndent();
@@ -467,36 +467,34 @@
   }
 
   public final class TextStyle {
-    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     ctor public TextStyle();
     method public androidx.ui.graphics.Color? component1();
-    method public androidx.ui.text.style.BaselineShift? component10();
-    method public androidx.ui.text.style.TextGeometricTransform? component11();
-    method public androidx.ui.text.LocaleList? component12();
-    method public androidx.ui.graphics.Color? component13();
-    method public androidx.ui.text.style.TextDecoration? component14();
-    method public androidx.ui.graphics.Shadow? component15();
-    method public androidx.ui.core.Sp component2();
-    method public Float? component3();
-    method public androidx.ui.text.font.FontWeight? component4();
-    method public androidx.ui.text.font.FontStyle? component5();
-    method public androidx.ui.text.font.FontSynthesis? component6();
-    method public androidx.ui.text.font.FontFamily? component7();
-    method public String? component8();
-    method public androidx.ui.core.Em? component9();
-    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    method public androidx.ui.text.style.TextGeometricTransform? component10();
+    method public androidx.ui.text.LocaleList? component11();
+    method public androidx.ui.graphics.Color? component12();
+    method public androidx.ui.text.style.TextDecoration? component13();
+    method public androidx.ui.graphics.Shadow? component14();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.font.FontWeight? component3();
+    method public androidx.ui.text.font.FontStyle? component4();
+    method public androidx.ui.text.font.FontSynthesis? component5();
+    method public androidx.ui.text.font.FontFamily? component6();
+    method public String? component7();
+    method public androidx.ui.core.TextUnit component8();
+    method public androidx.ui.text.style.BaselineShift? component9();
+    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     method public androidx.ui.graphics.Color? getBackground();
     method public androidx.ui.text.style.BaselineShift? getBaselineShift();
     method public androidx.ui.graphics.Color? getColor();
     method public androidx.ui.text.style.TextDecoration? getDecoration();
     method public androidx.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
-    method public androidx.ui.core.Sp getFontSize();
-    method public Float? getFontSizeScale();
+    method public androidx.ui.core.TextUnit getFontSize();
     method public androidx.ui.text.font.FontStyle? getFontStyle();
     method public androidx.ui.text.font.FontSynthesis? getFontSynthesis();
     method public androidx.ui.text.font.FontWeight? getFontWeight();
-    method public androidx.ui.core.Em? getLetterSpacing();
+    method public androidx.ui.core.TextUnit getLetterSpacing();
     method public androidx.ui.text.LocaleList? getLocaleList();
     method public androidx.ui.graphics.Shadow? getShadow();
     method public androidx.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
@@ -714,13 +712,13 @@
   }
 
   public final class TextIndent {
-    ctor public TextIndent(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
+    ctor public TextIndent(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
     ctor public TextIndent();
-    method public androidx.ui.core.Sp component1();
-    method public androidx.ui.core.Sp component2();
-    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
-    method public androidx.ui.core.Sp getFirstLine();
-    method public androidx.ui.core.Sp getRestLine();
+    method public androidx.ui.core.TextUnit component1();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
+    method public androidx.ui.core.TextUnit getFirstLine();
+    method public androidx.ui.core.TextUnit getRestLine();
   }
 
   public final class TextIndentKt {
diff --git a/ui/ui-text/api/restricted_current.txt b/ui/ui-text/api/restricted_current.txt
index 5157e30..8e81f5f 100644
--- a/ui/ui-text/api/restricted_current.txt
+++ b/ui/ui-text/api/restricted_current.txt
@@ -397,14 +397,14 @@
   }
 
   public final class ParagraphStyle {
-    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    ctor public ParagraphStyle(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
     ctor public ParagraphStyle();
     method public androidx.ui.text.style.TextAlign? component1();
     method public androidx.ui.text.style.TextDirectionAlgorithm? component2();
-    method public androidx.ui.core.Sp component3();
+    method public androidx.ui.core.TextUnit component3();
     method public androidx.ui.text.style.TextIndent? component4();
-    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.Sp lineHeight, androidx.ui.text.style.TextIndent? textIndent);
-    method public androidx.ui.core.Sp getLineHeight();
+    method public androidx.ui.text.ParagraphStyle copy(androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.core.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
+    method public androidx.ui.core.TextUnit getLineHeight();
     method public androidx.ui.text.style.TextAlign? getTextAlign();
     method public androidx.ui.text.style.TextDirectionAlgorithm? getTextDirectionAlgorithm();
     method public androidx.ui.text.style.TextIndent? getTextIndent();
@@ -467,36 +467,34 @@
   }
 
   public final class TextStyle {
-    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     ctor public TextStyle();
     method public androidx.ui.graphics.Color? component1();
-    method public androidx.ui.text.style.BaselineShift? component10();
-    method public androidx.ui.text.style.TextGeometricTransform? component11();
-    method public androidx.ui.text.LocaleList? component12();
-    method public androidx.ui.graphics.Color? component13();
-    method public androidx.ui.text.style.TextDecoration? component14();
-    method public androidx.ui.graphics.Shadow? component15();
-    method public androidx.ui.core.Sp component2();
-    method public Float? component3();
-    method public androidx.ui.text.font.FontWeight? component4();
-    method public androidx.ui.text.font.FontStyle? component5();
-    method public androidx.ui.text.font.FontSynthesis? component6();
-    method public androidx.ui.text.font.FontFamily? component7();
-    method public String? component8();
-    method public androidx.ui.core.Em? component9();
-    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.Sp fontSize, Float? fontSizeScale, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.Em? letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
+    method public androidx.ui.text.style.TextGeometricTransform? component10();
+    method public androidx.ui.text.LocaleList? component11();
+    method public androidx.ui.graphics.Color? component12();
+    method public androidx.ui.text.style.TextDecoration? component13();
+    method public androidx.ui.graphics.Shadow? component14();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.font.FontWeight? component3();
+    method public androidx.ui.text.font.FontStyle? component4();
+    method public androidx.ui.text.font.FontSynthesis? component5();
+    method public androidx.ui.text.font.FontFamily? component6();
+    method public String? component7();
+    method public androidx.ui.core.TextUnit component8();
+    method public androidx.ui.text.style.BaselineShift? component9();
+    method public androidx.ui.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.core.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.core.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? decoration, androidx.ui.graphics.Shadow? shadow);
     method public androidx.ui.graphics.Color? getBackground();
     method public androidx.ui.text.style.BaselineShift? getBaselineShift();
     method public androidx.ui.graphics.Color? getColor();
     method public androidx.ui.text.style.TextDecoration? getDecoration();
     method public androidx.ui.text.font.FontFamily? getFontFamily();
     method public String? getFontFeatureSettings();
-    method public androidx.ui.core.Sp getFontSize();
-    method public Float? getFontSizeScale();
+    method public androidx.ui.core.TextUnit getFontSize();
     method public androidx.ui.text.font.FontStyle? getFontStyle();
     method public androidx.ui.text.font.FontSynthesis? getFontSynthesis();
     method public androidx.ui.text.font.FontWeight? getFontWeight();
-    method public androidx.ui.core.Em? getLetterSpacing();
+    method public androidx.ui.core.TextUnit getLetterSpacing();
     method public androidx.ui.text.LocaleList? getLocaleList();
     method public androidx.ui.graphics.Shadow? getShadow();
     method public androidx.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
@@ -714,13 +712,13 @@
   }
 
   public final class TextIndent {
-    ctor public TextIndent(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
+    ctor public TextIndent(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
     ctor public TextIndent();
-    method public androidx.ui.core.Sp component1();
-    method public androidx.ui.core.Sp component2();
-    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.Sp firstLine, androidx.ui.core.Sp restLine);
-    method public androidx.ui.core.Sp getFirstLine();
-    method public androidx.ui.core.Sp getRestLine();
+    method public androidx.ui.core.TextUnit component1();
+    method public androidx.ui.core.TextUnit component2();
+    method public androidx.ui.text.style.TextIndent copy(androidx.ui.core.TextUnit firstLine, androidx.ui.core.TextUnit restLine);
+    method public androidx.ui.core.TextUnit getFirstLine();
+    method public androidx.ui.core.TextUnit getRestLine();
   }
 
   public final class TextIndentKt {
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt
index 4ef4b51..74fb9e3 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt
@@ -34,7 +34,6 @@
 import androidx.ui.graphics.Shadow
 import androidx.ui.text.TextStyle
 import androidx.ui.text.style.TextOverflow
-import androidx.ui.core.Sp
 import androidx.ui.core.em
 import androidx.ui.core.sp
 import androidx.ui.layout.ExpandedHeight
@@ -56,10 +55,10 @@
 val displayTextChinese = "文本演示"
 val displayTextArabic = "عرض النص"
 val displayTextHindi = "पाठ डेमो"
-val fontSize4: Sp = 16.sp
-val fontSize6: Sp = 20.sp
-val fontSize8: Sp = 25.sp
-val fontSize10: Sp = 30.sp
+val fontSize4 = 16.sp
+val fontSize6 = 20.sp
+val fontSize8 = 25.sp
+val fontSize10 = 30.sp
 
 @Composable
 fun TextDemo() {
@@ -507,7 +506,7 @@
         Span(style = TextStyle(fontSize = fontSize8)) {
             for (i in 4..12 step 4) {
                 val scale = i * 0.1f
-                Span("fontSizeScale=$scale\n", style = TextStyle(fontSizeScale = scale))
+                Span("fontSizeScale=$scale\n", style = TextStyle(fontSize = scale.em))
             }
         }
     }
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/MultiParagraphIntegrationTest.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/MultiParagraphIntegrationTest.kt
index 85692b7..7cb184f 100644
--- a/ui/ui-text/src/androidTest/java/androidx/ui/text/MultiParagraphIntegrationTest.kt
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/MultiParagraphIntegrationTest.kt
@@ -21,7 +21,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.ui.core.Density
 import androidx.ui.core.PxPosition
-import androidx.ui.core.Sp
+import androidx.ui.core.TextUnit
 import androidx.ui.core.px
 import androidx.ui.core.sp
 import androidx.ui.core.withDensity
@@ -1235,7 +1235,7 @@
 
     private fun simpleMultiParagraphIntrinsics(
         text: AnnotatedString,
-        fontSize: Sp = Sp.Inherit
+        fontSize: TextUnit = TextUnit.Inherit
     ): MultiParagraphIntrinsics {
         return MultiParagraphIntrinsics(
             text,
@@ -1253,7 +1253,7 @@
 
     private fun simpleMultiParagraph(
         text: String,
-        fontSize: Sp = Sp.Inherit,
+        fontSize: TextUnit = TextUnit.Inherit,
         paragraphStyle: ParagraphStyle = ParagraphStyle(),
         maxLines: Int? = null,
         width: Float = Float.MAX_VALUE
@@ -1277,7 +1277,7 @@
 
     private fun simpleMultiParagraph(
         text: AnnotatedString,
-        fontSize: Sp = Sp.Inherit,
+        fontSize: TextUnit = TextUnit.Inherit,
         paragraphStyle: ParagraphStyle = ParagraphStyle(),
         maxLines: Int? = null,
         width: Float = Float.MAX_VALUE
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt
index 0604136..8a04622 100644
--- a/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt
@@ -20,7 +20,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.ui.core.Density
 import androidx.ui.core.PxPosition
-import androidx.ui.core.Sp
+import androidx.ui.core.TextUnit
 import androidx.ui.core.em
 import androidx.ui.core.px
 import androidx.ui.core.sp
@@ -2254,8 +2254,8 @@
             val text = "abcde"
             val fontSize = 20.sp
             val fontSizeInPx = fontSize.toPx().value
-            val fontSizeScale = 0.5f
-            val textStyle = TextStyle(fontSizeScale = fontSizeScale)
+            val em = 0.5.em
+            val textStyle = TextStyle(fontSize = em)
 
             val paragraph = simpleParagraph(
                 text = text,
@@ -2264,7 +2264,7 @@
             )
 
             assertThat(paragraph.getLineRight(0))
-                .isEqualTo(text.length * fontSizeInPx * fontSizeScale)
+                .isEqualTo(text.length * fontSizeInPx * em.value)
         }
     }
 
@@ -2274,11 +2274,11 @@
             val text = "abcde"
             val fontSize = 20.sp
             val fontSizeInPx = fontSize.toPx().value
-            val fontSizeScale = 0.5f
-            val textStyle = TextStyle(fontSizeScale = fontSizeScale)
+            val em = 0.5f.em
+            val textStyle = TextStyle(fontSize = em)
 
-            val fontSizeScaleNested = 2f
-            val textStyleNested = TextStyle(fontSizeScale = fontSizeScaleNested)
+            val emNested = 2f.em
+            val textStyleNested = TextStyle(fontSize = emNested)
 
             val paragraph = simpleParagraph(
                 text = text,
@@ -2290,7 +2290,7 @@
             )
 
             assertThat(paragraph.getLineRight(0))
-                .isEqualTo(text.length * fontSizeInPx * fontSizeScale * fontSizeScaleNested)
+                .isEqualTo(text.length * fontSizeInPx * em.value * emNested.value)
         }
     }
 
@@ -2304,8 +2304,8 @@
             val fontSizeInPx = fontSize.toPx().value
             val fontSizeStyle = TextStyle(fontSize = fontSize)
 
-            val fontSizeScale = 0.5f
-            val fontSizeScaleStyle = TextStyle(fontSizeScale = fontSizeScale)
+            val em = 0.5f.em
+            val fontSizeScaleStyle = TextStyle(fontSize = em)
 
             val paragraph = simpleParagraph(
                 text = text,
@@ -2317,7 +2317,7 @@
             )
 
             assertThat(paragraph.getLineRight(0))
-                .isEqualTo(text.length * fontSizeInPx * fontSizeScale)
+                .isEqualTo(text.length * fontSizeInPx * em.value)
         }
     }
 
@@ -2331,8 +2331,8 @@
             val fontSizeInPx = fontSize.toPx().value
             val fontSizeStyle = TextStyle(fontSize = fontSize)
 
-            val fontSizeScale = 0.5f
-            val fontSizeScaleStyle = TextStyle(fontSizeScale = fontSizeScale)
+            val em = 0.5f.em
+            val fontSizeScaleStyle = TextStyle(fontSize = em)
 
             val paragraph = simpleParagraph(
                 text = text,
@@ -2357,11 +2357,11 @@
             val fontSizeInPx = fontSize.toPx().value
             val fontSizeStyle = TextStyle(fontSize = fontSize)
 
-            val fontSizeScale1 = 0.5f
-            val fontSizeScaleStyle1 = TextStyle(fontSizeScale = fontSizeScale1)
+            val em1 = 0.5f.em
+            val fontSizeScaleStyle1 = TextStyle(fontSize = em1)
 
-            val fontSizeScale2 = 2f
-            val fontSizeScaleStyle2 = TextStyle(fontSizeScale = fontSizeScale2)
+            val em2 = 2f.em
+            val fontSizeScaleStyle2 = TextStyle(fontSize = em2)
 
             val paragraph = simpleParagraph(
                 text = text,
@@ -2374,7 +2374,7 @@
             )
 
             assertThat(paragraph.getLineRight(0))
-                .isEqualTo(text.length * fontSizeInPx * fontSizeScale2)
+                .isEqualTo(text.length * fontSizeInPx * em2.value)
         }
     }
 
@@ -3174,9 +3174,9 @@
         text: String = "",
         textIndent: TextIndent? = null,
         textAlign: TextAlign? = null,
-        fontSize: Sp = Sp.Inherit,
+        fontSize: TextUnit = TextUnit.Inherit,
         maxLines: Int? = null,
-        lineHeight: Sp = Sp.Inherit,
+        lineHeight: TextUnit = TextUnit.Inherit,
         textStyles: List<AnnotatedString.Item<TextStyle>> = listOf(),
         fontFamily: FontFamily = fontFamilyMeasureFont,
         localeList: LocaleList? = null,
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntrinsicIntegrationTest.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntrinsicIntegrationTest.kt
index 886f37f..1ddfcf5 100644
--- a/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntrinsicIntegrationTest.kt
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntrinsicIntegrationTest.kt
@@ -19,7 +19,7 @@
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.ui.core.Density
-import androidx.ui.core.Sp
+import androidx.ui.core.TextUnit
 import androidx.ui.core.sp
 import androidx.ui.core.withDensity
 import androidx.ui.text.font.asFontFamily
@@ -96,7 +96,7 @@
         withDensity(defaultDensity) {
             val text = "a bb ccc"
             val fontSize = 12.sp
-            val styledFontSize = fontSize * 2
+            val styledFontSize = 24.sp
             val paragraph = paragraphIntrinsics(
                 text = text,
                 fontSize = fontSize,
@@ -182,7 +182,7 @@
         withDensity(defaultDensity) {
             val text = "a bb ccc"
             val fontSize = 12.sp
-            val styledFontSize = fontSize * 2
+            val styledFontSize = 24.sp
             val paragraph = paragraphIntrinsics(
                 text = text,
                 fontSize = fontSize,
@@ -203,7 +203,7 @@
     private fun paragraphIntrinsics(
         text: String = "",
         style: TextStyle? = null,
-        fontSize: Sp = 14.sp,
+        fontSize: TextUnit = 14.sp,
         textStyles: List<AnnotatedString.Item<TextStyle>> = listOf()
     ): ParagraphIntrinsics {
         return ParagraphIntrinsics(
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/platform/AndroidParagraphTest.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/platform/AndroidParagraphTest.kt
index 70f0de9..83631e5 100644
--- a/ui/ui-text/src/androidTest/java/androidx/ui/text/platform/AndroidParagraphTest.kt
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/platform/AndroidParagraphTest.kt
@@ -295,8 +295,8 @@
     @Test
     fun testAnnotatedString_setFontSizeScaleOnWholeText() {
         val text = "abcde"
-        val fontSizeScale = 2.0f
-        val textStyle = TextStyle(fontSizeScale = fontSizeScale)
+        val fontSizeScale = 2.0.em
+        val textStyle = TextStyle(fontSize = fontSizeScale)
 
         val paragraph = simpleParagraph(
             text = text,
@@ -305,15 +305,15 @@
         )
 
         assertThat(paragraph.charSequence).hasSpan(RelativeSizeSpan::class, 0, text.length) {
-            it.sizeChange == fontSizeScale
+            it.sizeChange == fontSizeScale.value
         }
     }
 
     @Test
     fun testAnnotatedString_setFontSizeScaleOnPartText() {
         val text = "abcde"
-        val fontSizeScale = 2.0f
-        val textStyle = TextStyle(fontSizeScale = fontSizeScale)
+        val fontSizeScale = 2.0f.em
+        val textStyle = TextStyle(fontSize = fontSizeScale)
 
         val paragraph = simpleParagraph(
             text = text,
@@ -322,7 +322,7 @@
         )
 
         assertThat(paragraph.charSequence).hasSpan(RelativeSizeSpan::class, 0, "abc".length) {
-            it.sizeChange == fontSizeScale
+            it.sizeChange == fontSizeScale.value
         }
     }
 
@@ -989,24 +989,6 @@
     }
 
     @Test
-    fun testTextStyle_fontSizeScale_appliedOnTextPaint() {
-        withDensity(defaultDensity) {
-            val fontSize = 100.sp
-            val fontSizeScale = 2f
-            val paragraph = simpleParagraph(
-                text = "",
-                textStyle = TextStyle(
-                    fontSize = fontSize,
-                    fontSizeScale = fontSizeScale
-                ),
-                constraints = ParagraphConstraints(width = 0.0f)
-            )
-
-            assertThat(paragraph.textPaint.textSize).isEqualTo(fontSize.toPx().value *fontSizeScale)
-        }
-    }
-
-    @Test
     fun testTextStyle_locale_appliedOnTextPaint() {
         val platformLocale = java.util.Locale.JAPANESE
         val localeList = LocaleList(platformLocale.toLanguageTag())
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt b/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt
index b6245a3..77be8c0 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt
@@ -17,7 +17,7 @@
 package androidx.ui.text
 
 import androidx.compose.Immutable
-import androidx.ui.core.Sp
+import androidx.ui.core.TextUnit
 import androidx.ui.text.style.TextAlign
 import androidx.ui.text.style.TextDirectionAlgorithm
 import androidx.ui.text.style.TextIndent
@@ -36,7 +36,7 @@
  * @param textDirectionAlgorithm The algorithm to be used to resolve the final text direction:
  * Left To Right or Right To Left.
  * @param textIndent The indentation of the paragraph.
- * @param lineHeight Line height for the [Paragraph] in [Sp] unit
+ * @param lineHeight Line height for the [Paragraph] in [TextUnit] unit, e.g. SP or EM.
  *
  * @see [Paragraph]
  * @see [AnnotatedString]
@@ -45,11 +45,11 @@
 data class ParagraphStyle constructor(
     val textAlign: TextAlign? = null,
     val textDirectionAlgorithm: TextDirectionAlgorithm? = null,
-    val lineHeight: Sp = Sp.Inherit,
+    val lineHeight: TextUnit = TextUnit.Inherit,
     val textIndent: TextIndent? = null
 ) {
     init {
-        if (lineHeight != Sp.Inherit) {
+        if (lineHeight != TextUnit.Inherit) {
             // Since we are checking if it's negative, no need to convert Sp into Px at this point.
             assert(lineHeight.value >= 0f) {
                 "lineHeight can't be negative (${lineHeight.value})"
@@ -67,7 +67,11 @@
         if (other == null) return this
 
         return ParagraphStyle(
-            lineHeight = if (other.lineHeight == Sp.Inherit) this.lineHeight else other.lineHeight,
+            lineHeight = if (other.lineHeight == TextUnit.Inherit) {
+                this.lineHeight
+            } else {
+                other.lineHeight
+            },
             textIndent = other.textIndent ?: this.textIndent,
             textAlign = other.textAlign ?: this.textAlign,
             textDirectionAlgorithm = other.textDirectionAlgorithm
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/TextDelegate.kt b/ui/ui-text/src/main/java/androidx/ui/text/TextDelegate.kt
index dc4bfbb..d9cceb4 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/TextDelegate.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/TextDelegate.kt
@@ -24,9 +24,8 @@
 import androidx.ui.core.IntPxSize
 import androidx.ui.core.LayoutDirection
 import androidx.ui.core.PxPosition
-import androidx.ui.core.Sp
+import androidx.ui.core.TextUnit
 import androidx.ui.core.constrain
-import androidx.ui.core.isInherit
 import androidx.ui.core.ipx
 import androidx.ui.core.sp
 import androidx.ui.engine.geometry.Offset
@@ -46,7 +45,7 @@
 import kotlin.math.roundToInt
 
 /** The default font size if none is specified. */
-private val DefaultFontSize: Sp = 14.sp
+private val DefaultFontSize: TextUnit = 14.sp
 
 /**
  * Resolve text style to be able to pass to underlying paragraphs.
@@ -56,7 +55,7 @@
 private fun resolveTextStyle(style: TextStyle?) =
     if (style == null) {
         TextStyle(fontSize = DefaultFontSize)
-    } else if (style.fontSize.isInherit()) {
+    } else if (style.fontSize.isInherit) {
         style.copy(fontSize = DefaultFontSize)
     } else {
         style
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/TextStyle.kt b/ui/ui-text/src/main/java/androidx/ui/text/TextStyle.kt
index 9636396..05ca93e 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/TextStyle.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/TextStyle.kt
@@ -17,16 +17,11 @@
 package androidx.ui.text
 
 import androidx.compose.Immutable
-import androidx.ui.core.Em
-import androidx.ui.core.Sp
-import androidx.ui.core.em
-import androidx.ui.core.isInherit
-import androidx.ui.core.lerp
-import androidx.ui.core.sp
+import androidx.ui.core.TextUnit
+import androidx.ui.core.lerpTextUnit
 import androidx.ui.graphics.Color
 import androidx.ui.graphics.Shadow
 import androidx.ui.graphics.lerp
-import androidx.ui.lerp
 import androidx.ui.text.font.FontFamily
 import androidx.ui.text.font.FontStyle
 import androidx.ui.text.font.FontSynthesis
@@ -44,10 +39,7 @@
  *
  * @param color The text color.
  * @param fontSize The size of glyphs (in logical pixels) to use when painting the text. This
- * may be [Sp.Inherit] for inheriting from another [TextStyle].
- * @param fontSizeScale The scale factor of the font size. When [fontSize] is also given in this
- *  TextStyle, the final fontSize will be the [fontSize] times this value.
- *  Otherwise, the final fontSize will be the current fontSize times this value.
+ * may be [TextUnit.Inherit] for inheriting from another [TextStyle].
  * @param fontWeight The typeface thickness to use when painting the text (e.g., bold).
  * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
  * @param fontSynthesis Whether to synthesize font weight and/or style when the requested weight or
@@ -69,14 +61,13 @@
 @Immutable
 data class TextStyle(
     val color: Color? = null,
-    val fontSize: Sp = Sp.Inherit,
-    val fontSizeScale: Float? = null,
+    val fontSize: TextUnit = TextUnit.Inherit,
     val fontWeight: FontWeight? = null,
     val fontStyle: FontStyle? = null,
     val fontSynthesis: FontSynthesis? = null,
     val fontFamily: FontFamily? = null,
     val fontFeatureSettings: String? = null,
-    val letterSpacing: Em? = null,
+    val letterSpacing: TextUnit = TextUnit.Inherit,
     val baselineShift: BaselineShift? = null,
     val textGeometricTransform: TextGeometricTransform? = null,
     val localeList: LocaleList? = null,
@@ -99,13 +90,16 @@
         return TextStyle(
             color = other.color ?: this.color,
             fontFamily = other.fontFamily ?: this.fontFamily,
-            fontSize = if (!other.fontSize.isInherit()) other.fontSize else this.fontSize,
-            fontSizeScale = other.fontSizeScale ?: this.fontSizeScale,
+            fontSize = if (!other.fontSize.isInherit) other.fontSize else this.fontSize,
             fontWeight = other.fontWeight ?: this.fontWeight,
             fontStyle = other.fontStyle ?: this.fontStyle,
             fontSynthesis = other.fontSynthesis ?: this.fontSynthesis,
             fontFeatureSettings = other.fontFeatureSettings ?: this.fontFeatureSettings,
-            letterSpacing = other.letterSpacing ?: this.letterSpacing,
+            letterSpacing = if (!other.letterSpacing.isInherit) {
+                other.letterSpacing
+            } else {
+                this.letterSpacing
+            },
             baselineShift = other.baselineShift ?: this.baselineShift,
             textGeometricTransform = other.textGeometricTransform ?: this.textGeometricTransform,
             localeList = other.localeList ?: this.localeList,
@@ -115,16 +109,13 @@
         )
     }
 }
-
 /**
- * @param a An sp value. Maybe [Sp.Inherit]
- * @param b An sp value. Maybe [Sp.Inherit]
+ * @param a An sp value. Maybe [TextUnit.Inherit]
+ * @param b An sp value. Maybe [TextUnit.Inherit]
  */
-private fun lerpSp(a: Sp, b: Sp, t: Float, default: Sp = 0f.sp): Sp {
-    if (a.isInherit() && b.isInherit()) return a
-    val start = if (a.isInherit()) default else a
-    val end = if (b.isInherit()) default else b
-    return androidx.ui.core.lerp(start, end, t)
+private fun lerpTextUnitInheritable(a: TextUnit, b: TextUnit, t: Float): TextUnit {
+    if (a.isInherit && b.isInherit) return a
+    return lerpTextUnit(a, b, t)
 }
 
 private fun <T> lerpDiscrete(a: T, b: T, t: Float): T = if (t < 0.5) a else b
@@ -150,12 +141,7 @@
             stop.fontFamily,
             fraction
         ),
-        fontSize = lerpSp(start.fontSize, stop.fontSize, fraction),
-        fontSizeScale = lerp(
-            start.fontSizeScale ?: 1.0f,
-            stop.fontSizeScale ?: 1.0f,
-            fraction
-        ),
+        fontSize = lerpTextUnitInheritable(start.fontSize, stop.fontSize, fraction),
         fontWeight = lerp(
             start.fontWeight ?: FontWeight.Normal,
             stop.fontWeight ?: FontWeight.Normal,
@@ -176,9 +162,9 @@
             stop.fontFeatureSettings,
             fraction
         ),
-        letterSpacing = lerp(
-            start.letterSpacing ?: 0.em,
-            stop.letterSpacing ?: 0.em,
+        letterSpacing = lerpTextUnitInheritable(
+            start.letterSpacing,
+            stop.letterSpacing,
             fraction
         ),
         baselineShift = lerp(
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraphHelper.kt b/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraphHelper.kt
index 2bea53a..06b1733 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraphHelper.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraphHelper.kt
@@ -18,7 +18,6 @@
 
 import android.graphics.Typeface
 import android.os.Build
-import android.os.LocaleList as AndroidLocaleList
 import android.text.SpannableString
 import android.text.Spanned
 import android.text.TextPaint
@@ -42,8 +41,9 @@
 import androidx.text.style.SkewXSpan
 import androidx.text.style.TypefaceSpan
 import androidx.ui.core.Density
-import androidx.ui.core.Sp
-import androidx.ui.core.isInherit
+import androidx.ui.core.TextUnit
+import androidx.ui.core.TextUnitType
+import androidx.ui.core.px
 import androidx.ui.core.sp
 import androidx.ui.core.withDensity
 import androidx.ui.graphics.toArgb
@@ -59,6 +59,7 @@
 import androidx.ui.text.style.TextIndent
 import kotlin.math.ceil
 import kotlin.math.roundToInt
+import android.os.LocaleList as AndroidLocaleList
 import java.util.Locale as JavaLocale
 
 internal fun TextPaint.applyTextStyle(
@@ -67,15 +68,14 @@
     density: Density
 ): TextStyle {
 
-    if (!style.fontSize.isInherit()) {
-        withDensity(density) {
+    when (style.fontSize.type) {
+        TextUnitType.Sp -> withDensity(density) {
             textSize = style.fontSize.toPx().value
         }
-    }
-
-    // fontSizeScale must be applied after fontSize is applied.
-    style.fontSizeScale?.let {
-        textSize *= it
+        TextUnitType.Em -> {
+            textSize *= style.fontSize.value
+        }
+        TextUnitType.Inherit -> {} // Do nothing
     }
 
     if (style.hasFontAttributes()) {
@@ -95,8 +95,15 @@
         color = it.toArgb()
     }
 
-    style.letterSpacing?.let {
-        letterSpacing = it.value
+    when (style.letterSpacing.type) {
+        TextUnitType.Sp -> withDensity(density) {
+            // Platform accept EM as a letter space. Convert Sp to Em
+            letterSpacing = style.letterSpacing.toPx().value / textSize
+        }
+        TextUnitType.Em -> {
+            letterSpacing = style.letterSpacing.value
+        }
+        TextUnitType.Inherit -> {} // Do nothing
     }
 
     style.fontFeatureSettings?.let {
@@ -139,7 +146,7 @@
 
 internal fun createStyledText(
     text: String,
-    lineHeight: Sp,
+    lineHeight: TextUnit,
     textIndent: TextIndent?,
     textStyles: List<AnnotatedString.Item<TextStyle>>,
     density: Density,
@@ -148,8 +155,8 @@
     if (textStyles.isEmpty() && textIndent == null) return text
     val spannableString = SpannableString(text)
 
-    if (lineHeight != Sp.Inherit) {
-        withDensity(density) {
+    when (lineHeight.type) {
+        TextUnitType.Sp -> withDensity(density) {
             spannableString.setSpan(
                 LineHeightSpan(ceil(lineHeight.toPx().value).toInt()),
                 0,
@@ -157,15 +164,36 @@
                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
             )
         }
+        TextUnitType.Em -> {
+            // Support line height with EM unit: b/144957855
+        }
+        TextUnitType.Inherit -> {} // Do nothing
     }
 
     textIndent?.let { indent ->
         if (indent.firstLine == 0.sp && indent.restLine == 0.sp) return@let
+        if (indent.firstLine.isInherit || indent.restLine.isInherit) return@let
         withDensity(density) {
+            val firstLine = when (indent.firstLine.type) {
+                TextUnitType.Sp -> indent.firstLine.toPx()
+                TextUnitType.Em -> {
+                    // Support indents with Em unit type: b/144958549
+                    0.px
+                }
+                TextUnitType.Inherit -> { 0.px } // do nothing
+            }
+            val restLine = when (indent.restLine.type) {
+                TextUnitType.Sp -> indent.restLine.toPx()
+                TextUnitType.Em -> {
+                    // Support indents with Em unit type: b/144958549
+                    0.px
+                }
+                TextUnitType.Inherit -> { 0.px } // do nothing
+            }
             spannableString.setSpan(
                 LeadingMarginSpan.Standard(
-                    indent.firstLine.toPx().value.toInt(),
-                    indent.restLine.toPx().value.toInt()
+                    firstLine.value.toInt(),
+                    restLine.value.toInt()
                 ),
                 0,
                 text.length,
@@ -220,8 +248,8 @@
             }
         }
 
-        if (!style.fontSize.isInherit()) {
-            withDensity(density) {
+        when (style.fontSize.type) {
+            TextUnitType.Sp -> withDensity(density) {
                 spannableString.setSpan(
                     AbsoluteSizeSpan(style.fontSize.toPx().value.roundToInt(), true),
                     start,
@@ -229,16 +257,15 @@
                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                 )
             }
-        }
-
-        // Be aware that fontSizeScale must be applied after fontSize.
-        style.fontSizeScale?.let {
-            spannableString.setSpan(
-                RelativeSizeSpan(it),
-                start,
-                end,
-                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
-            )
+            TextUnitType.Em -> {
+                spannableString.setSpan(
+                    RelativeSizeSpan(style.fontSize.value),
+                    start,
+                    end,
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
+                )
+            }
+            TextUnitType.Inherit -> {} // Do nothing
         }
 
         style.fontFeatureSettings?.let {
@@ -277,14 +304,21 @@
             )
         }
 
-        style.letterSpacing?.let {
-            spannableString.setSpan(
-                LetterSpacingSpan(it.value),
-                start,
-                end,
-                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
-            )
+        when (style.letterSpacing.type) {
+            TextUnitType.Sp -> {
+                // Support LetterSpacing with SP unit: b/144957997
+            }
+            TextUnitType.Em -> {
+                spannableString.setSpan(
+                    LetterSpacingSpan(style.letterSpacing.value),
+                    start,
+                    end,
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
+                )
+            }
+            TextUnitType.Inherit -> {}
         }
+
         style.localeList?.let {
             spannableString.setSpan(
                 if (Build.VERSION.SDK_INT >= 24) {
@@ -353,4 +387,4 @@
 
 @RequiresApi(api = 24)
 private fun LocaleList.toAndroidLocaleList(): AndroidLocaleList =
-    AndroidLocaleList(*map { it.toJavaLocale() }.toTypedArray())
\ No newline at end of file
+    AndroidLocaleList(*map { it.toJavaLocale() }.toTypedArray())
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/style/TextIndent.kt b/ui/ui-text/src/main/java/androidx/ui/text/style/TextIndent.kt
index ff42d41..3c774ec 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/style/TextIndent.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/style/TextIndent.kt
@@ -17,8 +17,8 @@
 package androidx.ui.text.style
 
 import androidx.compose.Immutable
-import androidx.ui.core.Sp
-import androidx.ui.core.lerp
+import androidx.ui.core.TextUnit
+import androidx.ui.core.lerpTextUnit
 import androidx.ui.core.sp
 
 /**
@@ -29,8 +29,8 @@
  */
 @Immutable
 data class TextIndent(
-    val firstLine: Sp = 0.sp,
-    val restLine: Sp = 0.sp
+    val firstLine: TextUnit = 0.sp,
+    val restLine: TextUnit = 0.sp
 )
 
 /**
@@ -40,7 +40,7 @@
  */
 fun lerp(start: TextIndent, stop: TextIndent, fraction: Float): TextIndent {
     return TextIndent(
-        lerp(start.firstLine, stop.firstLine, fraction),
-        lerp(start.restLine, stop.restLine, fraction)
+        lerpTextUnit(start.firstLine, stop.firstLine, fraction),
+        lerpTextUnit(start.restLine, stop.restLine, fraction)
     )
 }
diff --git a/ui/ui-text/src/test/java/androidx/ui/text/AnnotatedStringBuilderTest.kt b/ui/ui-text/src/test/java/androidx/ui/text/AnnotatedStringBuilderTest.kt
index 8515128..a48d2c9 100644
--- a/ui/ui-text/src/test/java/androidx/ui/text/AnnotatedStringBuilderTest.kt
+++ b/ui/ui-text/src/test/java/androidx/ui/text/AnnotatedStringBuilderTest.kt
@@ -16,7 +16,7 @@
 
 package androidx.ui.text
 
-import androidx.ui.core.Sp
+import androidx.ui.core.TextUnit
 import androidx.ui.core.em
 import androidx.ui.core.sp
 import androidx.ui.graphics.Color
@@ -566,7 +566,7 @@
     private fun createAnnotatedString(
         text: String,
         color: Color = Color.Red,
-        lineHeight: Sp = 20.sp
+        lineHeight: TextUnit = 20.sp
     ): AnnotatedString {
         return AnnotatedString(
             text = text,
diff --git a/ui/ui-text/src/test/java/androidx/ui/text/TextStyleTest.kt b/ui/ui-text/src/test/java/androidx/ui/text/TextStyleTest.kt
index d5ab64e..ce3b5d6 100644
--- a/ui/ui-text/src/test/java/androidx/ui/text/TextStyleTest.kt
+++ b/ui/ui-text/src/test/java/androidx/ui/text/TextStyleTest.kt
@@ -17,7 +17,6 @@
 package androidx.ui.text
 
 import androidx.ui.core.em
-import androidx.ui.core.isInherit
 import androidx.ui.core.sp
 import androidx.ui.text.style.BaselineShift
 import androidx.ui.text.font.FontStyle
@@ -42,10 +41,10 @@
         val textStyle = TextStyle()
 
         assertThat(textStyle.color).isNull()
-        assertThat(textStyle.fontSize.isInherit()).isTrue()
+        assertThat(textStyle.fontSize.isInherit).isTrue()
         assertThat(textStyle.fontWeight).isNull()
         assertThat(textStyle.fontStyle).isNull()
-        assertThat(textStyle.letterSpacing).isNull()
+        assertThat(textStyle.letterSpacing.isInherit).isTrue()
         assertThat(textStyle.localeList).isNull()
         assertThat(textStyle.background).isNull()
         assertThat(textStyle.decoration).isNull()
@@ -484,20 +483,6 @@
     }
 
     @Test
-    fun `lerp fontSizeScale with a and b are not Null`() {
-        val fontSizeScale1 = 2.0f
-        val fontSizeScale2 = 4.0f
-        val t = 0.8f
-        val textStyle1 = TextStyle(fontSizeScale = fontSizeScale1)
-        val textStyle2 = TextStyle(fontSizeScale = fontSizeScale2)
-
-        val newTextStyle = lerp(start = textStyle1, stop = textStyle2, fraction = t)
-
-        // a + (b - a) * t = 2.0f + (4.0f  - 2.0f) * 0.8f = 3.6f
-        assertThat(newTextStyle.fontSizeScale).isEqualTo(3.6f)
-    }
-
-    @Test
     fun `lerp fontWeight with a and b are not Null`() {
         val fontWeight1 = FontWeight.W200
         val fontWeight2 = FontWeight.W500
diff --git a/webkit/src/main/java/androidx/webkit/JsReplyProxy.java b/webkit/src/main/java/androidx/webkit/JsReplyProxy.java
index 488bb77..02a3ce5 100644
--- a/webkit/src/main/java/androidx/webkit/JsReplyProxy.java
+++ b/webkit/src/main/java/androidx/webkit/JsReplyProxy.java
@@ -21,9 +21,18 @@
 import androidx.annotation.RestrictTo;
 
 /**
- * AndroidX side JsReplyProxy class. An instance will be given by {@link
- * androidx.webkit.WebViewCompat.WebMessageListener#onPostMessage()}. See also {@link
- * androidx.webkit.WebViewCompat#addWebMessageListener()}.
+ * This class represents the JavaScript object injected by {@link
+ * WebViewCompat#addWebMessageListener(android.webkit.WebView, String, List,
+ * WebViewCompat.WebMessageListener) WebViewCompat#addWebMessageListener}. An instance will be given
+ * by {@link WebViewCompat.WebMessageListener#onPostMessage(android.webkit.WebView,
+ * WebMessageCompat, android.net.Uri, boolean, JsReplyProxy) WebMessageListener#onPostMessage}.
+ * The app can use {@link #postMessage(String)} to talk to the JavaScript context.
+ *
+ * <p>
+ * There is a 1:1 relationship between this object and the JavaScript object in a frame.
+ *
+ * @see WebViewCompat#addWebMessageListener(android.webkit.WebView, String, List,
+ * WebViewCompat.WebMessageListener).
  *
  * TODO(ctzsm): unhide
  * @hide
@@ -31,10 +40,10 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public abstract class JsReplyProxy {
     /**
-     * Post a String message to the injected JavaScript object which send this {@link
+     * Post a String message to the injected JavaScript object which sent this {@link
      * JsReplyProxy}.
      *
-     * @param message Message send from app to the corresponding JavaScript object.
+     * @param message The data to send to the JavaScript context.
      */
     @RequiresFeature(name = WebViewFeature.WEB_MESSAGE_LISTENER,
             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
diff --git a/webkit/src/main/java/androidx/webkit/WebViewCompat.java b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
index 9b20794..f6b3999 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewCompat.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
@@ -73,8 +73,8 @@
     }
 
     /**
-     * WebMessageListener callback interface. This is used to listen messages from the injected
-     * JavaScript object. See also {@link #addWebMessageListener()}.
+     * This listener receives messages sent on the JavaScript object which was injected by {@link
+     * #addWebMessageListener(WebView, String, List, WebViewCompat.WebMessageListener)}.
      *
      * TODO(ctzsm): unhide.
      * @hide
@@ -82,13 +82,13 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public interface WebMessageListener {
         /**
-         * Receives JavaScript {@code postMessage()} call information from the JavaScript object.
+         * Receives a message sent by a {@code postMessage()} on the injected JavaScript object.
          *
-         * @param view The {@link WebView} issued this message.
-         * @param message The {@link WebMessageCompat} message from JavaScript.
-         * @param sourceOrigin The origin of the frame where the message is from.
-         * @param isMainFrame If the message is from a main frame.
-         * @param replyProxy Used for reply message to the injected JavaScript object.
+         * @param view The {@link WebView} containing the frame which sent this message.
+         * @param message The message from JavaScript.
+         * @param sourceOrigin The origin of the frame that the message is from.
+         * @param isMainFrame {@code true} If the message is from the main frame.
+         * @param replyProxy Used to reply back to the JavaScript object.
          */
         @UiThread
         void onPostMessage(@NonNull WebView view, @NonNull WebMessageCompat message,
@@ -454,43 +454,136 @@
     }
 
     /**
-     * Adds a {@link WebMessageListener} to the {@android.webkit.WebView} and inject a JavaScript
-     * object that the {@link WebMessageListener} will listener on.
+     * Adds a {@link WebMessageListener} to the {@link WebView} and injects a JavaScript object into
+     * each frame that the {@link WebMessageListener} will listen on.
      *
      * <p>
-     * The injected JavaScript will be named as {@code jsObjectName}. We will inject the JavaScript
-     * object if the frame's origin matches any of the {@code allowedOriginRules} for every
-     * navigation after this call, the JavaScript object will be available immediately after the
-     * page loads.
+     * The injected JavaScript object will be named {@code jsObjectName} in the global scope. This
+     * will inject the JavaScript object in any frame whose origin matches {@code
+     * allowedOriginRules} for every navigation after this call, and the JavaScript object will be
+     * available immediately when the page begins to load.
      *
      * <p>
-     * This method could be called multiple times so multiple JavaScript objects will be injected.
+     * Each {@code allowedOriginRules} entry must follow the format below:
+     * <table>
+     * <tr><th>Rule</th><th>Description</th><th>Example</th></tr>
+     * <tr><td>{@code [ URL_SCHEME "://" ] HOSTNAME_PATTERN [ ":" PORT ]}</td>
+     * <td>Matches a hostname using a wildcard pattern, and an optional scheme and port
+     * restriction.</td>
+     * <td><ul>
+     * <li>{@code https://*.example.com} - Matches https://calendar.example.com and
+     * https://foo.bar.example.com but not https://example.com</li>
+     * <li>{@code https://foobar.com:8080} - Matches https:// URL on port 8080, whose normalized
+     * host is foobar.com.</li>
+     * <li>{@code *} - Matches URL of any scheme, port and domain.</li>
+     * </ul></td>
+     * </tr>
+     * <tr><td>{@code [ SCHEME "://" ] IP_LITERAL [ ":" PORT ]}</td>
+     * <td>Matches URLs that are IP address literals, and optional scheme and port restrictions.
+     * </td>
+     * <td><ul>
+     * <li>{@code https://127.0.0.1}</li>
+     * <li>{@code https://[::1]} or {@code https://[0:0::1]}- Matches any URL to the IPv6 loopback
+     * address.</li>
+     * <li>{@code https://[::1]:99} - Matches any https:// URL to the IPv6 loopback on port 99.</li>
+     * </ul></td>
+     * </tr>
+     * <tr><td>{@code IP_LITERAL "/" PREFIX_LENGTH_IN_BITS}</td><td>
+     * Matches any URL whose hostname is an IP literal, and falls between the given address
+     * range.</td>
+     * <td><ul>
+     * <li>{@code 192.168.0.0/16}</li>
+     * <li>{@code fefe:13::abc/33} - Note that there are no brackets on the IPv6 literal.</li>
+     * </ul></td>
+     * </tr>
+     * </table>
      *
      * <p>
-     * Note that this is a powerful API, the JavaScript object will be injected when the frame's
-     * origin matches any one of the allowed origins. If a wildcard {@code "*"} is provided, it will
-     * inject JavaScript object to all frames. App should try to void to use the wildcard as much as
-     * possible, instead, passing rules that only matches trusted URLs is highly recommended.
+     * Note that this is a powerful API, as the JavaScript object will be injected when the frame's
+     * origin matches any one of the allowed origins. The HTTPS scheme is strongly recommended for
+     * security; allowing HTTP origins exposes the injected object to any potential network-based
+     * attackers. If a wildcard {@code "*"} is provided, it will inject the JavaScript object to all
+     * frames. A wildcard should only be used if the app wants <b>any</b> third party web page to be
+     * able to use the injected object. When using a wildcard, the app must treat received messages
+     * as untrustworthy and validate any data carefully.
      *
+     * <p>
+     * This method can be called multiple times to inject multiple JavaScript objects.
+     *
+     * <p>
+     * Let's say the injected JavaScript object is named {@code myObject}. We will have following
+     * methods on that object once it is available to use:
+     * <pre class="prettyprint">
+     * // message needs to be a JavaScript String, MessagePorts is an optional parameter.
+     * myObject.postMessage(message[, MessagePorts])
+     *
+     * // To receive the message posted from the app side. event has a "data" property, which is the
+     * // message string from the app side.
+     * myObject.onmessage(event)
+     *
+     * // To be compatible with DOM EventTarget's addEventListener, it accepts type and listener
+     * // parameters, where type can be only "message" type and listener can only be a JavaScript
+     * // function for myObject. An event object will be passed to listener with a "data" property,
+     * // which is the message string from the app side.
+     * myObject.addEventListener(type, listener)
+     *
+     * // To be compatible with DOM EventTarget's removeEventListener, it accepts type and listener
+     * // parameters, where type can be only "message" type and listener can only be a JavaScript
+     * // function for myObject.
+     * myObject.removeEventListener(type, listener)
+     * </pre>
+     *
+     * <p>
+     * We start the communication between JavaScript and the app from the JavaScript side. In order
+     * to send message from the app to JavaScript, it needs to post a message from JavaScript first,
+     * so the app will have a {@link JsReplyProxy} object to respond. Example:
+     * <pre class="prettyprint">
+     * // Web page (in JavaScript)
+     * myObject.onmessage = function(event) {
+     *   // prints "Got it!" when we receive the app's response.
+     *   console.log(event.data);
+     * }
+     * myObject.postMessage("I'm ready!");
+     *
+     * // App (in Java)
+     * WebMessageListener myListener = new WebMessageListener() {
+     *   &#064;Override
+     *   public void onPostMessage(WebView view, WebMessageCompat message, Uri sourceOrigin,
+     *            boolean isMainFrame, JsReplyProxy replyProxy) {
+     *     // do something about view, message, sourceOrigin and isMainFrame.
+     *     replyProxy.postMessage("Got it!");
+     *   }
+     * };
+     * WebViewCompat.addWebMessageListener(webView, "myObject", rules, myListener);
+     * </pre>
+     *
+     * <p>
+     * This method should only be called if {@link WebViewFeature#isFeatureSupported(String)}
+     * returns true for {@link WebViewFeature#WEB_MESSAGE_LISTENER}.
+     *
+     * @param webView The {@link WebView} instance that we are interacting with.
      * @param jsObjectName The name for the injected JavaScript object for this {@link
      *         WebMessageListener}.
      * @param allowedOriginRules A list of matching rules for the allowed origins.
-     * @param listener The {@link WebMessageListener} to be called when received onPostMessage().
-     *
+     * @param listener The {@link WebMessageListener WebMessageListener} to handle postMessage()
+     *         calls on the JavaScript object.
      * @throws IllegalArgumentException If one of the {@code allowedOriginRules} is invalid.
      *
+     * @see JsReplyProxy
+     * @see WebMessageListener
+     *
      * //TODO(ctzsm): unhide
      * @hide
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @RequiresFeature(name = WebViewFeature.WEB_MESSAGE_LISTENER,
             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
-    public static void addWebMessageListener(@NonNull WebView webview, @NonNull String jsObjectName,
+    public static void addWebMessageListener(@NonNull WebView webView, @NonNull String jsObjectName,
             @NonNull List<String> allowedOriginRules, @NonNull WebMessageListener listener) {
         final WebViewFeatureInternal feature =
                 WebViewFeatureInternal.getFeature(WebViewFeature.WEB_MESSAGE_LISTENER);
         if (feature.isSupportedByWebView()) {
-            getProvider(webview).addWebMessageListener(
+            getProvider(webView).addWebMessageListener(
                     jsObjectName, allowedOriginRules.toArray(new String[0]), listener);
         } else {
             throw WebViewFeatureInternal.getUnsupportedOperationException();
@@ -498,14 +591,22 @@
     }
 
     /**
-     * Removes the {@link WebMessageListener} associated with the {@code jsObjectName}.
+     * Removes the {@link WebMessageListener WebMessageListener} associated with {@code
+     * jsObjectName}.
      *
      * <p>
      * Note that after this call, the injected JavaScript object is still in the JavaScript context,
-     * however any message send after this call won't reach to the {@link WebMessageListener}.
+     * however any message sent after this call won't reach the {@link WebMessageListener
+     * WebMessageListener}.
      *
-     * @param jsObjectName The JavaScript object's name that passed to {@link
-     *         #addWebMessageListener()} previously.
+     * <p>
+     * This method should only be called if {@link WebViewFeature#isFeatureSupported(String)}
+     * returns true for {@link WebViewFeature#WEB_MESSAGE_LISTENER}.
+     *
+     * @param jsObjectName The JavaScript object's name that was previously passed to {@link
+     *         #addWebMessageListener(WebView, String,  List, WebMessageListener)}.
+     *
+     * @see #addWebMessageListener(WebView, String, List, WebMessageListener)
      *
      * //TODO(ctzsm): unhide
      * @hide
diff --git a/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index 67fc876..d0a690a 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -428,10 +428,9 @@
 
     /**
      * Feature for {@link #isFeatureSupported(String)}.
-     * This feature covers
-     * {@link androidx.webkit.WebViewCompat#setWebMessageListener(android.webkit.WebView,
-     * androidx.webkit.WebViewCompat.WebMessageListener, String, String[])}
-     * {@link androidx.webkit.WebViewCompat#removeWebMessageListener()}
+     * This feature covers {@link WebViewCompat#addWebMessageListener(android.webkit.WebView,
+     * String, List, WebViewCompat.WebMessageListener)} and {@link
+     * WebViewCompat#removeWebMessageListener(android.webkit.WebView, String)}.
      *
      * TODO(ctzsm): unhide
      * @hide
diff --git a/work/workmanager-gcm/api/2.3.0-rc01.txt b/work/workmanager-gcm/api/2.3.0-rc01.txt
new file mode 100644
index 0000000..da4f6cc
--- /dev/null
+++ b/work/workmanager-gcm/api/2.3.0-rc01.txt
@@ -0,0 +1 @@
+// Signature format: 3.0
diff --git a/work/workmanager-gcm/api/public_plus_experimental_2.3.0-rc01.txt b/work/workmanager-gcm/api/public_plus_experimental_2.3.0-rc01.txt
new file mode 100644
index 0000000..da4f6cc
--- /dev/null
+++ b/work/workmanager-gcm/api/public_plus_experimental_2.3.0-rc01.txt
@@ -0,0 +1 @@
+// Signature format: 3.0
diff --git a/work/workmanager-gcm/api/res-2.3.0-rc01.txt b/work/workmanager-gcm/api/res-2.3.0-rc01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-gcm/api/res-2.3.0-rc01.txt
diff --git a/work/workmanager-gcm/api/restricted_2.3.0-rc01.txt b/work/workmanager-gcm/api/restricted_2.3.0-rc01.txt
new file mode 100644
index 0000000..da4f6cc
--- /dev/null
+++ b/work/workmanager-gcm/api/restricted_2.3.0-rc01.txt
@@ -0,0 +1 @@
+// Signature format: 3.0
diff --git a/work/workmanager-ktx/api/2.3.0-rc01.txt b/work/workmanager-ktx/api/2.3.0-rc01.txt
new file mode 100644
index 0000000..5873b39
--- /dev/null
+++ b/work/workmanager-ktx/api/2.3.0-rc01.txt
@@ -0,0 +1,45 @@
+// Signature format: 3.0
+package androidx.work {
+
+  public abstract class CoroutineWorker extends androidx.work.ListenableWorker {
+    ctor public CoroutineWorker(android.content.Context appContext, androidx.work.WorkerParameters params);
+    method public abstract suspend Object doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
+    method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
+    method public final void onStopped();
+    method public final suspend Object! setForeground(androidx.work.ForegroundInfo foregroundInfo, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public final suspend Object! setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
+    property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
+  }
+
+  public final class DataKt {
+    ctor public DataKt();
+    method public static inline <reified T> boolean hasKeyWithValueOfType(androidx.work.Data, String key);
+    method public static inline androidx.work.Data workDataOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class ListenableFutureKt {
+    ctor public ListenableFutureKt();
+  }
+
+  public final class OneTimeWorkRequestKt {
+    ctor public OneTimeWorkRequestKt();
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.OneTimeWorkRequest.Builder OneTimeWorkRequestBuilder();
+    method public static inline androidx.work.OneTimeWorkRequest.Builder setInputMerger(androidx.work.OneTimeWorkRequest.Builder, kotlin.reflect.KClass<? extends androidx.work.InputMerger> inputMerger);
+  }
+
+  public final class OperationKt {
+    ctor public OperationKt();
+    method public static suspend inline Object! await(androidx.work.Operation, kotlin.coroutines.Continuation<? super androidx.work.Operation.State.SUCCESS> p);
+  }
+
+  public final class PeriodicWorkRequestKt {
+    ctor public PeriodicWorkRequestKt();
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit);
+    method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(java.time.Duration repeatInterval);
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit, long flexTimeInterval, java.util.concurrent.TimeUnit flexTimeIntervalUnit);
+    method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(java.time.Duration repeatInterval, java.time.Duration flexTimeInterval);
+  }
+
+}
+
diff --git a/work/workmanager-ktx/api/public_plus_experimental_2.3.0-rc01.txt b/work/workmanager-ktx/api/public_plus_experimental_2.3.0-rc01.txt
new file mode 100644
index 0000000..5873b39
--- /dev/null
+++ b/work/workmanager-ktx/api/public_plus_experimental_2.3.0-rc01.txt
@@ -0,0 +1,45 @@
+// Signature format: 3.0
+package androidx.work {
+
+  public abstract class CoroutineWorker extends androidx.work.ListenableWorker {
+    ctor public CoroutineWorker(android.content.Context appContext, androidx.work.WorkerParameters params);
+    method public abstract suspend Object doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
+    method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
+    method public final void onStopped();
+    method public final suspend Object! setForeground(androidx.work.ForegroundInfo foregroundInfo, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public final suspend Object! setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
+    property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
+  }
+
+  public final class DataKt {
+    ctor public DataKt();
+    method public static inline <reified T> boolean hasKeyWithValueOfType(androidx.work.Data, String key);
+    method public static inline androidx.work.Data workDataOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class ListenableFutureKt {
+    ctor public ListenableFutureKt();
+  }
+
+  public final class OneTimeWorkRequestKt {
+    ctor public OneTimeWorkRequestKt();
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.OneTimeWorkRequest.Builder OneTimeWorkRequestBuilder();
+    method public static inline androidx.work.OneTimeWorkRequest.Builder setInputMerger(androidx.work.OneTimeWorkRequest.Builder, kotlin.reflect.KClass<? extends androidx.work.InputMerger> inputMerger);
+  }
+
+  public final class OperationKt {
+    ctor public OperationKt();
+    method public static suspend inline Object! await(androidx.work.Operation, kotlin.coroutines.Continuation<? super androidx.work.Operation.State.SUCCESS> p);
+  }
+
+  public final class PeriodicWorkRequestKt {
+    ctor public PeriodicWorkRequestKt();
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit);
+    method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(java.time.Duration repeatInterval);
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit, long flexTimeInterval, java.util.concurrent.TimeUnit flexTimeIntervalUnit);
+    method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(java.time.Duration repeatInterval, java.time.Duration flexTimeInterval);
+  }
+
+}
+
diff --git a/work/workmanager-ktx/api/res-2.3.0-rc01.txt b/work/workmanager-ktx/api/res-2.3.0-rc01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-ktx/api/res-2.3.0-rc01.txt
diff --git a/work/workmanager-ktx/api/restricted_2.3.0-rc01.txt b/work/workmanager-ktx/api/restricted_2.3.0-rc01.txt
new file mode 100644
index 0000000..075851a
--- /dev/null
+++ b/work/workmanager-ktx/api/restricted_2.3.0-rc01.txt
@@ -0,0 +1,51 @@
+// Signature format: 3.0
+package androidx.work {
+
+  public abstract class CoroutineWorker extends androidx.work.ListenableWorker {
+    ctor public CoroutineWorker(android.content.Context appContext, androidx.work.WorkerParameters params);
+    method public abstract suspend Object doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
+    method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
+    method public final void onStopped();
+    method public final suspend Object! setForeground(androidx.work.ForegroundInfo foregroundInfo, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public final suspend Object! setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
+    property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
+  }
+
+  public final class DataKt {
+    ctor public DataKt();
+    method public static inline <reified T> boolean hasKeyWithValueOfType(androidx.work.Data, String key);
+    method public static inline androidx.work.Data workDataOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public enum DirectExecutor implements java.util.concurrent.Executor {
+    method public void execute(Runnable command);
+    enum_constant public static final androidx.work.DirectExecutor INSTANCE;
+  }
+
+  public final class ListenableFutureKt {
+    ctor public ListenableFutureKt();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static suspend inline <R> Object! await(com.google.common.util.concurrent.ListenableFuture<R>, kotlin.coroutines.Continuation<? super R> p);
+  }
+
+  public final class OneTimeWorkRequestKt {
+    ctor public OneTimeWorkRequestKt();
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.OneTimeWorkRequest.Builder OneTimeWorkRequestBuilder();
+    method public static inline androidx.work.OneTimeWorkRequest.Builder setInputMerger(androidx.work.OneTimeWorkRequest.Builder, kotlin.reflect.KClass<? extends androidx.work.InputMerger> inputMerger);
+  }
+
+  public final class OperationKt {
+    ctor public OperationKt();
+    method public static suspend inline Object! await(androidx.work.Operation, kotlin.coroutines.Continuation<? super androidx.work.Operation.State.SUCCESS> p);
+  }
+
+  public final class PeriodicWorkRequestKt {
+    ctor public PeriodicWorkRequestKt();
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit);
+    method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(java.time.Duration repeatInterval);
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit, long flexTimeInterval, java.util.concurrent.TimeUnit flexTimeIntervalUnit);
+    method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder PeriodicWorkRequestBuilder(java.time.Duration repeatInterval, java.time.Duration flexTimeInterval);
+  }
+
+}
+
diff --git a/work/workmanager-rxjava2/api/2.3.0-rc01.txt b/work/workmanager-rxjava2/api/2.3.0-rc01.txt
new file mode 100644
index 0000000..757ca4c
--- /dev/null
+++ b/work/workmanager-rxjava2/api/2.3.0-rc01.txt
@@ -0,0 +1,13 @@
+// Signature format: 3.0
+package androidx.work {
+
+  public abstract class RxWorker extends androidx.work.ListenableWorker {
+    ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
+    method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
+    method protected io.reactivex.Scheduler getBackgroundScheduler();
+    method public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+  }
+
+}
+
diff --git a/work/workmanager-rxjava2/api/public_plus_experimental_2.3.0-rc01.txt b/work/workmanager-rxjava2/api/public_plus_experimental_2.3.0-rc01.txt
new file mode 100644
index 0000000..757ca4c
--- /dev/null
+++ b/work/workmanager-rxjava2/api/public_plus_experimental_2.3.0-rc01.txt
@@ -0,0 +1,13 @@
+// Signature format: 3.0
+package androidx.work {
+
+  public abstract class RxWorker extends androidx.work.ListenableWorker {
+    ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
+    method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
+    method protected io.reactivex.Scheduler getBackgroundScheduler();
+    method public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+  }
+
+}
+
diff --git a/work/workmanager-rxjava2/api/res-2.3.0-rc01.txt b/work/workmanager-rxjava2/api/res-2.3.0-rc01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-rxjava2/api/res-2.3.0-rc01.txt
diff --git a/work/workmanager-rxjava2/api/restricted_2.3.0-rc01.txt b/work/workmanager-rxjava2/api/restricted_2.3.0-rc01.txt
new file mode 100644
index 0000000..757ca4c
--- /dev/null
+++ b/work/workmanager-rxjava2/api/restricted_2.3.0-rc01.txt
@@ -0,0 +1,13 @@
+// Signature format: 3.0
+package androidx.work {
+
+  public abstract class RxWorker extends androidx.work.ListenableWorker {
+    ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
+    method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
+    method protected io.reactivex.Scheduler getBackgroundScheduler();
+    method public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+  }
+
+}
+
diff --git a/work/workmanager-testing/api/2.3.0-rc01.txt b/work/workmanager-testing/api/2.3.0-rc01.txt
new file mode 100644
index 0000000..2f8dc82
--- /dev/null
+++ b/work/workmanager-testing/api/2.3.0-rc01.txt
@@ -0,0 +1,54 @@
+// Signature format: 3.0
+package androidx.work.testing {
+
+  public class SynchronousExecutor implements java.util.concurrent.Executor {
+    ctor public SynchronousExecutor();
+    method public void execute(Runnable);
+  }
+
+  public interface TestDriver {
+    method public void setAllConstraintsMet(java.util.UUID);
+    method public void setInitialDelayMet(java.util.UUID);
+    method public void setPeriodDelayMet(java.util.UUID);
+  }
+
+  public class TestListenableWorkerBuilder<W extends androidx.work.ListenableWorker> {
+    method public W build();
+    method public static androidx.work.testing.TestListenableWorkerBuilder from(android.content.Context, androidx.work.WorkRequest);
+    method public static <W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W!> from(android.content.Context, Class<W!>);
+    method public androidx.work.testing.TestListenableWorkerBuilder setForegroundUpdater(androidx.work.ForegroundUpdater);
+    method public androidx.work.testing.TestListenableWorkerBuilder setId(java.util.UUID);
+    method public androidx.work.testing.TestListenableWorkerBuilder setInputData(androidx.work.Data);
+    method @RequiresApi(28) public androidx.work.testing.TestListenableWorkerBuilder setNetwork(android.net.Network);
+    method public androidx.work.testing.TestListenableWorkerBuilder setProgressUpdater(androidx.work.ProgressUpdater);
+    method public androidx.work.testing.TestListenableWorkerBuilder setRunAttemptCount(int);
+    method public androidx.work.testing.TestListenableWorkerBuilder setTags(java.util.List<java.lang.String!>);
+    method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder setTriggeredContentAuthorities(java.util.List<java.lang.String!>);
+    method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder setTriggeredContentUris(java.util.List<android.net.Uri!>);
+    method public androidx.work.testing.TestListenableWorkerBuilder setWorkerFactory(androidx.work.WorkerFactory);
+  }
+
+  public final class TestListenableWorkerBuilderKt {
+    ctor public TestListenableWorkerBuilderKt();
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W> TestListenableWorkerBuilder(android.content.Context context, androidx.work.Data inputData = androidx.work.Data.EMPTY, java.util.List<java.lang.String> tags = emptyList(), int runAttemptCount = 1, java.util.List<? extends android.net.Uri> triggeredContentUris = emptyList(), java.util.List<java.lang.String> triggeredContentAuthorities = emptyList());
+  }
+
+  public class TestWorkerBuilder<W extends androidx.work.Worker> extends androidx.work.testing.TestListenableWorkerBuilder<W> {
+    method public static androidx.work.testing.TestWorkerBuilder<? extends androidx.work.Worker> from(android.content.Context, androidx.work.WorkRequest, java.util.concurrent.Executor);
+    method public static <W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W!> from(android.content.Context, Class<W!>, java.util.concurrent.Executor);
+  }
+
+  public final class TestWorkerBuilderKt {
+    ctor public TestWorkerBuilderKt();
+    method public static inline <reified W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W> TestWorkerBuilder(android.content.Context context, java.util.concurrent.Executor executor, androidx.work.Data inputData = androidx.work.Data.EMPTY, java.util.List<java.lang.String> tags = emptyList(), int runAttemptCount = 1, java.util.List<? extends android.net.Uri> triggeredContentUris = emptyList(), java.util.List<java.lang.String> triggeredContentAuthorities = emptyList());
+  }
+
+  public final class WorkManagerTestInitHelper {
+    method @Deprecated public static androidx.work.testing.TestDriver? getTestDriver();
+    method public static androidx.work.testing.TestDriver? getTestDriver(android.content.Context);
+    method public static void initializeTestWorkManager(android.content.Context);
+    method public static void initializeTestWorkManager(android.content.Context, androidx.work.Configuration);
+  }
+
+}
+
diff --git a/work/workmanager-testing/api/public_plus_experimental_2.3.0-rc01.txt b/work/workmanager-testing/api/public_plus_experimental_2.3.0-rc01.txt
new file mode 100644
index 0000000..2f8dc82
--- /dev/null
+++ b/work/workmanager-testing/api/public_plus_experimental_2.3.0-rc01.txt
@@ -0,0 +1,54 @@
+// Signature format: 3.0
+package androidx.work.testing {
+
+  public class SynchronousExecutor implements java.util.concurrent.Executor {
+    ctor public SynchronousExecutor();
+    method public void execute(Runnable);
+  }
+
+  public interface TestDriver {
+    method public void setAllConstraintsMet(java.util.UUID);
+    method public void setInitialDelayMet(java.util.UUID);
+    method public void setPeriodDelayMet(java.util.UUID);
+  }
+
+  public class TestListenableWorkerBuilder<W extends androidx.work.ListenableWorker> {
+    method public W build();
+    method public static androidx.work.testing.TestListenableWorkerBuilder from(android.content.Context, androidx.work.WorkRequest);
+    method public static <W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W!> from(android.content.Context, Class<W!>);
+    method public androidx.work.testing.TestListenableWorkerBuilder setForegroundUpdater(androidx.work.ForegroundUpdater);
+    method public androidx.work.testing.TestListenableWorkerBuilder setId(java.util.UUID);
+    method public androidx.work.testing.TestListenableWorkerBuilder setInputData(androidx.work.Data);
+    method @RequiresApi(28) public androidx.work.testing.TestListenableWorkerBuilder setNetwork(android.net.Network);
+    method public androidx.work.testing.TestListenableWorkerBuilder setProgressUpdater(androidx.work.ProgressUpdater);
+    method public androidx.work.testing.TestListenableWorkerBuilder setRunAttemptCount(int);
+    method public androidx.work.testing.TestListenableWorkerBuilder setTags(java.util.List<java.lang.String!>);
+    method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder setTriggeredContentAuthorities(java.util.List<java.lang.String!>);
+    method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder setTriggeredContentUris(java.util.List<android.net.Uri!>);
+    method public androidx.work.testing.TestListenableWorkerBuilder setWorkerFactory(androidx.work.WorkerFactory);
+  }
+
+  public final class TestListenableWorkerBuilderKt {
+    ctor public TestListenableWorkerBuilderKt();
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W> TestListenableWorkerBuilder(android.content.Context context, androidx.work.Data inputData = androidx.work.Data.EMPTY, java.util.List<java.lang.String> tags = emptyList(), int runAttemptCount = 1, java.util.List<? extends android.net.Uri> triggeredContentUris = emptyList(), java.util.List<java.lang.String> triggeredContentAuthorities = emptyList());
+  }
+
+  public class TestWorkerBuilder<W extends androidx.work.Worker> extends androidx.work.testing.TestListenableWorkerBuilder<W> {
+    method public static androidx.work.testing.TestWorkerBuilder<? extends androidx.work.Worker> from(android.content.Context, androidx.work.WorkRequest, java.util.concurrent.Executor);
+    method public static <W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W!> from(android.content.Context, Class<W!>, java.util.concurrent.Executor);
+  }
+
+  public final class TestWorkerBuilderKt {
+    ctor public TestWorkerBuilderKt();
+    method public static inline <reified W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W> TestWorkerBuilder(android.content.Context context, java.util.concurrent.Executor executor, androidx.work.Data inputData = androidx.work.Data.EMPTY, java.util.List<java.lang.String> tags = emptyList(), int runAttemptCount = 1, java.util.List<? extends android.net.Uri> triggeredContentUris = emptyList(), java.util.List<java.lang.String> triggeredContentAuthorities = emptyList());
+  }
+
+  public final class WorkManagerTestInitHelper {
+    method @Deprecated public static androidx.work.testing.TestDriver? getTestDriver();
+    method public static androidx.work.testing.TestDriver? getTestDriver(android.content.Context);
+    method public static void initializeTestWorkManager(android.content.Context);
+    method public static void initializeTestWorkManager(android.content.Context, androidx.work.Configuration);
+  }
+
+}
+
diff --git a/work/workmanager-testing/api/res-2.3.0-rc01.txt b/work/workmanager-testing/api/res-2.3.0-rc01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-testing/api/res-2.3.0-rc01.txt
diff --git a/work/workmanager-testing/api/restricted_2.3.0-rc01.txt b/work/workmanager-testing/api/restricted_2.3.0-rc01.txt
new file mode 100644
index 0000000..c866b6a
--- /dev/null
+++ b/work/workmanager-testing/api/restricted_2.3.0-rc01.txt
@@ -0,0 +1,64 @@
+// Signature format: 3.0
+package androidx.work.testing {
+
+  public class SynchronousExecutor implements java.util.concurrent.Executor {
+    ctor public SynchronousExecutor();
+    method public void execute(Runnable);
+  }
+
+  public interface TestDriver {
+    method public void setAllConstraintsMet(java.util.UUID);
+    method public void setInitialDelayMet(java.util.UUID);
+    method public void setPeriodDelayMet(java.util.UUID);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class TestForegroundUpdater implements androidx.work.ForegroundUpdater {
+    ctor public TestForegroundUpdater();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setForegroundAsync(android.content.Context, java.util.UUID, androidx.work.ForegroundInfo);
+  }
+
+  public class TestListenableWorkerBuilder<W extends androidx.work.ListenableWorker> {
+    method public W build();
+    method public static androidx.work.testing.TestListenableWorkerBuilder from(android.content.Context, androidx.work.WorkRequest);
+    method public static <W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W!> from(android.content.Context, Class<W!>);
+    method public androidx.work.testing.TestListenableWorkerBuilder setForegroundUpdater(androidx.work.ForegroundUpdater);
+    method public androidx.work.testing.TestListenableWorkerBuilder setId(java.util.UUID);
+    method public androidx.work.testing.TestListenableWorkerBuilder setInputData(androidx.work.Data);
+    method @RequiresApi(28) public androidx.work.testing.TestListenableWorkerBuilder setNetwork(android.net.Network);
+    method public androidx.work.testing.TestListenableWorkerBuilder setProgressUpdater(androidx.work.ProgressUpdater);
+    method public androidx.work.testing.TestListenableWorkerBuilder setRunAttemptCount(int);
+    method public androidx.work.testing.TestListenableWorkerBuilder setTags(java.util.List<java.lang.String!>);
+    method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder setTriggeredContentAuthorities(java.util.List<java.lang.String!>);
+    method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder setTriggeredContentUris(java.util.List<android.net.Uri!>);
+    method public androidx.work.testing.TestListenableWorkerBuilder setWorkerFactory(androidx.work.WorkerFactory);
+  }
+
+  public final class TestListenableWorkerBuilderKt {
+    ctor public TestListenableWorkerBuilderKt();
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W> TestListenableWorkerBuilder(android.content.Context context, androidx.work.Data inputData = androidx.work.Data.EMPTY, java.util.List<java.lang.String> tags = emptyList(), int runAttemptCount = 1, java.util.List<? extends android.net.Uri> triggeredContentUris = emptyList(), java.util.List<java.lang.String> triggeredContentAuthorities = emptyList());
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class TestProgressUpdater implements androidx.work.ProgressUpdater {
+    ctor public TestProgressUpdater();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> updateProgress(android.content.Context, java.util.UUID, androidx.work.Data);
+  }
+
+  public class TestWorkerBuilder<W extends androidx.work.Worker> extends androidx.work.testing.TestListenableWorkerBuilder<W> {
+    method public static androidx.work.testing.TestWorkerBuilder<? extends androidx.work.Worker> from(android.content.Context, androidx.work.WorkRequest, java.util.concurrent.Executor);
+    method public static <W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W!> from(android.content.Context, Class<W!>, java.util.concurrent.Executor);
+  }
+
+  public final class TestWorkerBuilderKt {
+    ctor public TestWorkerBuilderKt();
+    method public static inline <reified W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W> TestWorkerBuilder(android.content.Context context, java.util.concurrent.Executor executor, androidx.work.Data inputData = androidx.work.Data.EMPTY, java.util.List<java.lang.String> tags = emptyList(), int runAttemptCount = 1, java.util.List<? extends android.net.Uri> triggeredContentUris = emptyList(), java.util.List<java.lang.String> triggeredContentAuthorities = emptyList());
+  }
+
+  public final class WorkManagerTestInitHelper {
+    method @Deprecated public static androidx.work.testing.TestDriver? getTestDriver();
+    method public static androidx.work.testing.TestDriver? getTestDriver(android.content.Context);
+    method public static void initializeTestWorkManager(android.content.Context);
+    method public static void initializeTestWorkManager(android.content.Context, androidx.work.Configuration);
+  }
+
+}
+
diff --git a/work/workmanager/api/2.3.0-rc01.txt b/work/workmanager/api/2.3.0-rc01.txt
new file mode 100644
index 0000000..46dc320
--- /dev/null
+++ b/work/workmanager/api/2.3.0-rc01.txt
@@ -0,0 +1,331 @@
+// Signature format: 3.0
+package androidx.work {
+
+  public final class ArrayCreatingInputMerger extends androidx.work.InputMerger {
+    ctor public ArrayCreatingInputMerger();
+    method public androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+  }
+
+  public enum BackoffPolicy {
+    enum_constant public static final androidx.work.BackoffPolicy EXPONENTIAL;
+    enum_constant public static final androidx.work.BackoffPolicy LINEAR;
+  }
+
+  public final class Configuration {
+    method public java.util.concurrent.Executor getExecutor();
+    method public androidx.work.InputMergerFactory getInputMergerFactory();
+    method public int getMaxJobSchedulerId();
+    method public int getMinJobSchedulerId();
+    method public java.util.concurrent.Executor getTaskExecutor();
+    method public androidx.work.WorkerFactory getWorkerFactory();
+    field public static final int MIN_SCHEDULER_LIMIT = 20; // 0x14
+  }
+
+  public static final class Configuration.Builder {
+    ctor public Configuration.Builder();
+    method public androidx.work.Configuration build();
+    method public androidx.work.Configuration.Builder setExecutor(java.util.concurrent.Executor);
+    method public androidx.work.Configuration.Builder setInputMergerFactory(androidx.work.InputMergerFactory);
+    method public androidx.work.Configuration.Builder setJobSchedulerJobIdRange(int, int);
+    method public androidx.work.Configuration.Builder setMaxSchedulerLimit(int);
+    method public androidx.work.Configuration.Builder setMinimumLoggingLevel(int);
+    method public androidx.work.Configuration.Builder setTaskExecutor(java.util.concurrent.Executor);
+    method public androidx.work.Configuration.Builder setWorkerFactory(androidx.work.WorkerFactory);
+  }
+
+  public static interface Configuration.Provider {
+    method public androidx.work.Configuration getWorkManagerConfiguration();
+  }
+
+  public final class Constraints {
+    ctor public Constraints(androidx.work.Constraints);
+    method public androidx.work.NetworkType getRequiredNetworkType();
+    method public boolean requiresBatteryNotLow();
+    method public boolean requiresCharging();
+    method @RequiresApi(23) public boolean requiresDeviceIdle();
+    method public boolean requiresStorageNotLow();
+    field public static final androidx.work.Constraints! NONE;
+  }
+
+  public static final class Constraints.Builder {
+    ctor public Constraints.Builder();
+    method @RequiresApi(24) public androidx.work.Constraints.Builder addContentUriTrigger(android.net.Uri, boolean);
+    method public androidx.work.Constraints build();
+    method public androidx.work.Constraints.Builder setRequiredNetworkType(androidx.work.NetworkType);
+    method public androidx.work.Constraints.Builder setRequiresBatteryNotLow(boolean);
+    method public androidx.work.Constraints.Builder setRequiresCharging(boolean);
+    method @RequiresApi(23) public androidx.work.Constraints.Builder setRequiresDeviceIdle(boolean);
+    method public androidx.work.Constraints.Builder setRequiresStorageNotLow(boolean);
+    method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(java.time.Duration!);
+    method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(java.time.Duration!);
+  }
+
+  public final class Data {
+    ctor public Data(androidx.work.Data);
+    method public static androidx.work.Data fromByteArray(byte[]);
+    method public boolean getBoolean(String, boolean);
+    method public boolean[]? getBooleanArray(String);
+    method public byte getByte(String, byte);
+    method public byte[]? getByteArray(String);
+    method public double getDouble(String, double);
+    method public double[]? getDoubleArray(String);
+    method public float getFloat(String, float);
+    method public float[]? getFloatArray(String);
+    method public int getInt(String, int);
+    method public int[]? getIntArray(String);
+    method public java.util.Map<java.lang.String!,java.lang.Object!> getKeyValueMap();
+    method public long getLong(String, long);
+    method public long[]? getLongArray(String);
+    method public String? getString(String);
+    method public String![]? getStringArray(String);
+    method public <T> boolean hasKeyWithValueOfType(String, Class<T!>);
+    method public byte[] toByteArray();
+    field public static final androidx.work.Data! EMPTY;
+    field public static final int MAX_DATA_BYTES = 10240; // 0x2800
+  }
+
+  public static final class Data.Builder {
+    ctor public Data.Builder();
+    method public androidx.work.Data build();
+    method public androidx.work.Data.Builder putAll(androidx.work.Data);
+    method public androidx.work.Data.Builder putAll(java.util.Map<java.lang.String!,java.lang.Object!>);
+    method public androidx.work.Data.Builder putBoolean(String, boolean);
+    method public androidx.work.Data.Builder putBooleanArray(String, boolean[]);
+    method public androidx.work.Data.Builder putByte(String, byte);
+    method public androidx.work.Data.Builder putByteArray(String, byte[]);
+    method public androidx.work.Data.Builder putDouble(String, double);
+    method public androidx.work.Data.Builder putDoubleArray(String, double[]);
+    method public androidx.work.Data.Builder putFloat(String, float);
+    method public androidx.work.Data.Builder putFloatArray(String, float[]);
+    method public androidx.work.Data.Builder putInt(String, int);
+    method public androidx.work.Data.Builder putIntArray(String, int[]);
+    method public androidx.work.Data.Builder putLong(String, long);
+    method public androidx.work.Data.Builder putLongArray(String, long[]);
+    method public androidx.work.Data.Builder putString(String, String?);
+    method public androidx.work.Data.Builder putStringArray(String, String![]);
+  }
+
+  public class DelegatingWorkerFactory extends androidx.work.WorkerFactory {
+    ctor public DelegatingWorkerFactory();
+    method public final void addFactory(androidx.work.WorkerFactory);
+    method public final androidx.work.ListenableWorker? createWorker(android.content.Context, String, androidx.work.WorkerParameters);
+  }
+
+  public enum ExistingPeriodicWorkPolicy {
+    enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy KEEP;
+    enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy REPLACE;
+  }
+
+  public enum ExistingWorkPolicy {
+    enum_constant public static final androidx.work.ExistingWorkPolicy APPEND;
+    enum_constant public static final androidx.work.ExistingWorkPolicy KEEP;
+    enum_constant public static final androidx.work.ExistingWorkPolicy REPLACE;
+  }
+
+  public final class ForegroundInfo {
+    ctor public ForegroundInfo(android.app.Notification);
+    ctor public ForegroundInfo(android.app.Notification, int);
+    method public int getForegroundServiceType();
+    method public android.app.Notification getNotification();
+  }
+
+  public interface ForegroundUpdater {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setForegroundAsync(android.content.Context, java.util.UUID, androidx.work.ForegroundInfo);
+  }
+
+  public abstract class InputMerger {
+    ctor public InputMerger();
+    method public abstract androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+  }
+
+  public abstract class InputMergerFactory {
+    ctor public InputMergerFactory();
+    method public abstract androidx.work.InputMerger? createInputMerger(String);
+  }
+
+  public abstract class ListenableWorker {
+    ctor @Keep public ListenableWorker(android.content.Context, androidx.work.WorkerParameters);
+    method public final android.content.Context getApplicationContext();
+    method public final java.util.UUID getId();
+    method public final androidx.work.Data getInputData();
+    method @RequiresApi(28) public final android.net.Network? getNetwork();
+    method @IntRange(from=0) public final int getRunAttemptCount();
+    method public final java.util.Set<java.lang.String!> getTags();
+    method @RequiresApi(24) public final java.util.List<java.lang.String!> getTriggeredContentAuthorities();
+    method @RequiresApi(24) public final java.util.List<android.net.Uri!> getTriggeredContentUris();
+    method public final boolean isStopped();
+    method public void onStopped();
+    method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setForegroundAsync(androidx.work.ForegroundInfo);
+    method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgressAsync(androidx.work.Data);
+    method @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+  }
+
+  public abstract static class ListenableWorker.Result {
+    method public static androidx.work.ListenableWorker.Result failure();
+    method public static androidx.work.ListenableWorker.Result failure(androidx.work.Data);
+    method public static androidx.work.ListenableWorker.Result retry();
+    method public static androidx.work.ListenableWorker.Result success();
+    method public static androidx.work.ListenableWorker.Result success(androidx.work.Data);
+  }
+
+  public enum NetworkType {
+    enum_constant public static final androidx.work.NetworkType CONNECTED;
+    enum_constant public static final androidx.work.NetworkType METERED;
+    enum_constant public static final androidx.work.NetworkType NOT_REQUIRED;
+    enum_constant public static final androidx.work.NetworkType NOT_ROAMING;
+    enum_constant public static final androidx.work.NetworkType UNMETERED;
+  }
+
+  public final class OneTimeWorkRequest extends androidx.work.WorkRequest {
+    method public static androidx.work.OneTimeWorkRequest from(Class<? extends androidx.work.ListenableWorker>);
+    method public static java.util.List<androidx.work.OneTimeWorkRequest!> from(java.util.List<java.lang.Class<? extends androidx.work.ListenableWorker>!>);
+  }
+
+  public static final class OneTimeWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.OneTimeWorkRequest.Builder,androidx.work.OneTimeWorkRequest> {
+    ctor public OneTimeWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>);
+    method public androidx.work.OneTimeWorkRequest.Builder setInputMerger(Class<? extends androidx.work.InputMerger>);
+  }
+
+  public interface Operation {
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.work.Operation.State.SUCCESS!> getResult();
+    method public androidx.lifecycle.LiveData<androidx.work.Operation.State!> getState();
+  }
+
+  public abstract static class Operation.State {
+  }
+
+  public static final class Operation.State.FAILURE extends androidx.work.Operation.State {
+    ctor public Operation.State.FAILURE(Throwable);
+    method public Throwable getThrowable();
+  }
+
+  public static final class Operation.State.IN_PROGRESS extends androidx.work.Operation.State {
+  }
+
+  public static final class Operation.State.SUCCESS extends androidx.work.Operation.State {
+  }
+
+  public final class OverwritingInputMerger extends androidx.work.InputMerger {
+    ctor public OverwritingInputMerger();
+    method public androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+  }
+
+  public final class PeriodicWorkRequest extends androidx.work.WorkRequest {
+    field public static final long MIN_PERIODIC_FLEX_MILLIS = 300000L; // 0x493e0L
+    field public static final long MIN_PERIODIC_INTERVAL_MILLIS = 900000L; // 0xdbba0L
+  }
+
+  public static final class PeriodicWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.PeriodicWorkRequest.Builder,androidx.work.PeriodicWorkRequest> {
+    ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit);
+    ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration);
+    ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit);
+    ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration, java.time.Duration);
+  }
+
+  public interface ProgressUpdater {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> updateProgress(android.content.Context, java.util.UUID, androidx.work.Data);
+  }
+
+  public abstract class WorkContinuation {
+    ctor public WorkContinuation();
+    method public static androidx.work.WorkContinuation combine(java.util.List<androidx.work.WorkContinuation!>);
+    method public abstract androidx.work.Operation enqueue();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfos();
+    method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosLiveData();
+    method public final androidx.work.WorkContinuation then(androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.WorkContinuation then(java.util.List<androidx.work.OneTimeWorkRequest!>);
+  }
+
+  public final class WorkInfo {
+    method public java.util.UUID getId();
+    method public androidx.work.Data getOutputData();
+    method public androidx.work.Data getProgress();
+    method @IntRange(from=0) public int getRunAttemptCount();
+    method public androidx.work.WorkInfo.State getState();
+    method public java.util.Set<java.lang.String!> getTags();
+  }
+
+  public enum WorkInfo.State {
+    method public boolean isFinished();
+    enum_constant public static final androidx.work.WorkInfo.State BLOCKED;
+    enum_constant public static final androidx.work.WorkInfo.State CANCELLED;
+    enum_constant public static final androidx.work.WorkInfo.State ENQUEUED;
+    enum_constant public static final androidx.work.WorkInfo.State FAILED;
+    enum_constant public static final androidx.work.WorkInfo.State RUNNING;
+    enum_constant public static final androidx.work.WorkInfo.State SUCCEEDED;
+  }
+
+  public abstract class WorkManager {
+    method public final androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+    method public final androidx.work.WorkContinuation beginWith(androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.WorkContinuation beginWith(java.util.List<androidx.work.OneTimeWorkRequest!>);
+    method public abstract androidx.work.Operation cancelAllWork();
+    method public abstract androidx.work.Operation cancelAllWorkByTag(String);
+    method public abstract androidx.work.Operation cancelUniqueWork(String);
+    method public abstract androidx.work.Operation cancelWorkById(java.util.UUID);
+    method public abstract android.app.PendingIntent createCancelPendingIntent(java.util.UUID);
+    method public final androidx.work.Operation enqueue(androidx.work.WorkRequest);
+    method public abstract androidx.work.Operation enqueue(java.util.List<? extends androidx.work.WorkRequest>);
+    method public abstract androidx.work.Operation enqueueUniquePeriodicWork(String, androidx.work.ExistingPeriodicWorkPolicy, androidx.work.PeriodicWorkRequest);
+    method public androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+    method @Deprecated public static androidx.work.WorkManager getInstance();
+    method public static androidx.work.WorkManager getInstance(android.content.Context);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Long!> getLastCancelAllTimeMillis();
+    method public abstract androidx.lifecycle.LiveData<java.lang.Long!> getLastCancelAllTimeMillisLiveData();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.WorkInfo!> getWorkInfoById(java.util.UUID);
+    method public abstract androidx.lifecycle.LiveData<androidx.work.WorkInfo!> getWorkInfoByIdLiveData(java.util.UUID);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosByTag(String);
+    method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosByTagLiveData(String);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosForUniqueWork(String);
+    method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosForUniqueWorkLiveData(String);
+    method public static void initialize(android.content.Context, androidx.work.Configuration);
+    method public abstract androidx.work.Operation pruneWork();
+  }
+
+  public abstract class WorkRequest {
+    method public java.util.UUID getId();
+    field public static final long DEFAULT_BACKOFF_DELAY_MILLIS = 30000L; // 0x7530L
+    field public static final long MAX_BACKOFF_MILLIS = 18000000L; // 0x112a880L
+    field public static final long MIN_BACKOFF_MILLIS = 10000L; // 0x2710L
+  }
+
+  public abstract static class WorkRequest.Builder<B extends androidx.work.WorkRequest.Builder<?, ?>, W extends androidx.work.WorkRequest> {
+    method public final B addTag(String);
+    method public final W build();
+    method public final B keepResultsForAtLeast(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public final B keepResultsForAtLeast(java.time.Duration);
+    method public final B setBackoffCriteria(androidx.work.BackoffPolicy, long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public final B setBackoffCriteria(androidx.work.BackoffPolicy, java.time.Duration);
+    method public final B setConstraints(androidx.work.Constraints);
+    method public B setInitialDelay(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public B setInitialDelay(java.time.Duration);
+    method public final B setInputData(androidx.work.Data);
+  }
+
+  public abstract class Worker extends androidx.work.ListenableWorker {
+    ctor @Keep public Worker(android.content.Context, androidx.work.WorkerParameters);
+    method @WorkerThread public abstract androidx.work.ListenableWorker.Result doWork();
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+  }
+
+  public abstract class WorkerFactory {
+    ctor public WorkerFactory();
+    method public abstract androidx.work.ListenableWorker? createWorker(android.content.Context, String, androidx.work.WorkerParameters);
+  }
+
+  public final class WorkerParameters {
+    method public java.util.UUID getId();
+    method public androidx.work.Data getInputData();
+    method @RequiresApi(28) public android.net.Network? getNetwork();
+    method @IntRange(from=0) public int getRunAttemptCount();
+    method public java.util.Set<java.lang.String!> getTags();
+    method @RequiresApi(24) public java.util.List<java.lang.String!> getTriggeredContentAuthorities();
+    method @RequiresApi(24) public java.util.List<android.net.Uri!> getTriggeredContentUris();
+  }
+
+}
+
diff --git a/work/workmanager/api/public_plus_experimental_2.3.0-rc01.txt b/work/workmanager/api/public_plus_experimental_2.3.0-rc01.txt
new file mode 100644
index 0000000..46dc320
--- /dev/null
+++ b/work/workmanager/api/public_plus_experimental_2.3.0-rc01.txt
@@ -0,0 +1,331 @@
+// Signature format: 3.0
+package androidx.work {
+
+  public final class ArrayCreatingInputMerger extends androidx.work.InputMerger {
+    ctor public ArrayCreatingInputMerger();
+    method public androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+  }
+
+  public enum BackoffPolicy {
+    enum_constant public static final androidx.work.BackoffPolicy EXPONENTIAL;
+    enum_constant public static final androidx.work.BackoffPolicy LINEAR;
+  }
+
+  public final class Configuration {
+    method public java.util.concurrent.Executor getExecutor();
+    method public androidx.work.InputMergerFactory getInputMergerFactory();
+    method public int getMaxJobSchedulerId();
+    method public int getMinJobSchedulerId();
+    method public java.util.concurrent.Executor getTaskExecutor();
+    method public androidx.work.WorkerFactory getWorkerFactory();
+    field public static final int MIN_SCHEDULER_LIMIT = 20; // 0x14
+  }
+
+  public static final class Configuration.Builder {
+    ctor public Configuration.Builder();
+    method public androidx.work.Configuration build();
+    method public androidx.work.Configuration.Builder setExecutor(java.util.concurrent.Executor);
+    method public androidx.work.Configuration.Builder setInputMergerFactory(androidx.work.InputMergerFactory);
+    method public androidx.work.Configuration.Builder setJobSchedulerJobIdRange(int, int);
+    method public androidx.work.Configuration.Builder setMaxSchedulerLimit(int);
+    method public androidx.work.Configuration.Builder setMinimumLoggingLevel(int);
+    method public androidx.work.Configuration.Builder setTaskExecutor(java.util.concurrent.Executor);
+    method public androidx.work.Configuration.Builder setWorkerFactory(androidx.work.WorkerFactory);
+  }
+
+  public static interface Configuration.Provider {
+    method public androidx.work.Configuration getWorkManagerConfiguration();
+  }
+
+  public final class Constraints {
+    ctor public Constraints(androidx.work.Constraints);
+    method public androidx.work.NetworkType getRequiredNetworkType();
+    method public boolean requiresBatteryNotLow();
+    method public boolean requiresCharging();
+    method @RequiresApi(23) public boolean requiresDeviceIdle();
+    method public boolean requiresStorageNotLow();
+    field public static final androidx.work.Constraints! NONE;
+  }
+
+  public static final class Constraints.Builder {
+    ctor public Constraints.Builder();
+    method @RequiresApi(24) public androidx.work.Constraints.Builder addContentUriTrigger(android.net.Uri, boolean);
+    method public androidx.work.Constraints build();
+    method public androidx.work.Constraints.Builder setRequiredNetworkType(androidx.work.NetworkType);
+    method public androidx.work.Constraints.Builder setRequiresBatteryNotLow(boolean);
+    method public androidx.work.Constraints.Builder setRequiresCharging(boolean);
+    method @RequiresApi(23) public androidx.work.Constraints.Builder setRequiresDeviceIdle(boolean);
+    method public androidx.work.Constraints.Builder setRequiresStorageNotLow(boolean);
+    method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(java.time.Duration!);
+    method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(java.time.Duration!);
+  }
+
+  public final class Data {
+    ctor public Data(androidx.work.Data);
+    method public static androidx.work.Data fromByteArray(byte[]);
+    method public boolean getBoolean(String, boolean);
+    method public boolean[]? getBooleanArray(String);
+    method public byte getByte(String, byte);
+    method public byte[]? getByteArray(String);
+    method public double getDouble(String, double);
+    method public double[]? getDoubleArray(String);
+    method public float getFloat(String, float);
+    method public float[]? getFloatArray(String);
+    method public int getInt(String, int);
+    method public int[]? getIntArray(String);
+    method public java.util.Map<java.lang.String!,java.lang.Object!> getKeyValueMap();
+    method public long getLong(String, long);
+    method public long[]? getLongArray(String);
+    method public String? getString(String);
+    method public String![]? getStringArray(String);
+    method public <T> boolean hasKeyWithValueOfType(String, Class<T!>);
+    method public byte[] toByteArray();
+    field public static final androidx.work.Data! EMPTY;
+    field public static final int MAX_DATA_BYTES = 10240; // 0x2800
+  }
+
+  public static final class Data.Builder {
+    ctor public Data.Builder();
+    method public androidx.work.Data build();
+    method public androidx.work.Data.Builder putAll(androidx.work.Data);
+    method public androidx.work.Data.Builder putAll(java.util.Map<java.lang.String!,java.lang.Object!>);
+    method public androidx.work.Data.Builder putBoolean(String, boolean);
+    method public androidx.work.Data.Builder putBooleanArray(String, boolean[]);
+    method public androidx.work.Data.Builder putByte(String, byte);
+    method public androidx.work.Data.Builder putByteArray(String, byte[]);
+    method public androidx.work.Data.Builder putDouble(String, double);
+    method public androidx.work.Data.Builder putDoubleArray(String, double[]);
+    method public androidx.work.Data.Builder putFloat(String, float);
+    method public androidx.work.Data.Builder putFloatArray(String, float[]);
+    method public androidx.work.Data.Builder putInt(String, int);
+    method public androidx.work.Data.Builder putIntArray(String, int[]);
+    method public androidx.work.Data.Builder putLong(String, long);
+    method public androidx.work.Data.Builder putLongArray(String, long[]);
+    method public androidx.work.Data.Builder putString(String, String?);
+    method public androidx.work.Data.Builder putStringArray(String, String![]);
+  }
+
+  public class DelegatingWorkerFactory extends androidx.work.WorkerFactory {
+    ctor public DelegatingWorkerFactory();
+    method public final void addFactory(androidx.work.WorkerFactory);
+    method public final androidx.work.ListenableWorker? createWorker(android.content.Context, String, androidx.work.WorkerParameters);
+  }
+
+  public enum ExistingPeriodicWorkPolicy {
+    enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy KEEP;
+    enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy REPLACE;
+  }
+
+  public enum ExistingWorkPolicy {
+    enum_constant public static final androidx.work.ExistingWorkPolicy APPEND;
+    enum_constant public static final androidx.work.ExistingWorkPolicy KEEP;
+    enum_constant public static final androidx.work.ExistingWorkPolicy REPLACE;
+  }
+
+  public final class ForegroundInfo {
+    ctor public ForegroundInfo(android.app.Notification);
+    ctor public ForegroundInfo(android.app.Notification, int);
+    method public int getForegroundServiceType();
+    method public android.app.Notification getNotification();
+  }
+
+  public interface ForegroundUpdater {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setForegroundAsync(android.content.Context, java.util.UUID, androidx.work.ForegroundInfo);
+  }
+
+  public abstract class InputMerger {
+    ctor public InputMerger();
+    method public abstract androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+  }
+
+  public abstract class InputMergerFactory {
+    ctor public InputMergerFactory();
+    method public abstract androidx.work.InputMerger? createInputMerger(String);
+  }
+
+  public abstract class ListenableWorker {
+    ctor @Keep public ListenableWorker(android.content.Context, androidx.work.WorkerParameters);
+    method public final android.content.Context getApplicationContext();
+    method public final java.util.UUID getId();
+    method public final androidx.work.Data getInputData();
+    method @RequiresApi(28) public final android.net.Network? getNetwork();
+    method @IntRange(from=0) public final int getRunAttemptCount();
+    method public final java.util.Set<java.lang.String!> getTags();
+    method @RequiresApi(24) public final java.util.List<java.lang.String!> getTriggeredContentAuthorities();
+    method @RequiresApi(24) public final java.util.List<android.net.Uri!> getTriggeredContentUris();
+    method public final boolean isStopped();
+    method public void onStopped();
+    method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setForegroundAsync(androidx.work.ForegroundInfo);
+    method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgressAsync(androidx.work.Data);
+    method @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+  }
+
+  public abstract static class ListenableWorker.Result {
+    method public static androidx.work.ListenableWorker.Result failure();
+    method public static androidx.work.ListenableWorker.Result failure(androidx.work.Data);
+    method public static androidx.work.ListenableWorker.Result retry();
+    method public static androidx.work.ListenableWorker.Result success();
+    method public static androidx.work.ListenableWorker.Result success(androidx.work.Data);
+  }
+
+  public enum NetworkType {
+    enum_constant public static final androidx.work.NetworkType CONNECTED;
+    enum_constant public static final androidx.work.NetworkType METERED;
+    enum_constant public static final androidx.work.NetworkType NOT_REQUIRED;
+    enum_constant public static final androidx.work.NetworkType NOT_ROAMING;
+    enum_constant public static final androidx.work.NetworkType UNMETERED;
+  }
+
+  public final class OneTimeWorkRequest extends androidx.work.WorkRequest {
+    method public static androidx.work.OneTimeWorkRequest from(Class<? extends androidx.work.ListenableWorker>);
+    method public static java.util.List<androidx.work.OneTimeWorkRequest!> from(java.util.List<java.lang.Class<? extends androidx.work.ListenableWorker>!>);
+  }
+
+  public static final class OneTimeWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.OneTimeWorkRequest.Builder,androidx.work.OneTimeWorkRequest> {
+    ctor public OneTimeWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>);
+    method public androidx.work.OneTimeWorkRequest.Builder setInputMerger(Class<? extends androidx.work.InputMerger>);
+  }
+
+  public interface Operation {
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.work.Operation.State.SUCCESS!> getResult();
+    method public androidx.lifecycle.LiveData<androidx.work.Operation.State!> getState();
+  }
+
+  public abstract static class Operation.State {
+  }
+
+  public static final class Operation.State.FAILURE extends androidx.work.Operation.State {
+    ctor public Operation.State.FAILURE(Throwable);
+    method public Throwable getThrowable();
+  }
+
+  public static final class Operation.State.IN_PROGRESS extends androidx.work.Operation.State {
+  }
+
+  public static final class Operation.State.SUCCESS extends androidx.work.Operation.State {
+  }
+
+  public final class OverwritingInputMerger extends androidx.work.InputMerger {
+    ctor public OverwritingInputMerger();
+    method public androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+  }
+
+  public final class PeriodicWorkRequest extends androidx.work.WorkRequest {
+    field public static final long MIN_PERIODIC_FLEX_MILLIS = 300000L; // 0x493e0L
+    field public static final long MIN_PERIODIC_INTERVAL_MILLIS = 900000L; // 0xdbba0L
+  }
+
+  public static final class PeriodicWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.PeriodicWorkRequest.Builder,androidx.work.PeriodicWorkRequest> {
+    ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit);
+    ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration);
+    ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit);
+    ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration, java.time.Duration);
+  }
+
+  public interface ProgressUpdater {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> updateProgress(android.content.Context, java.util.UUID, androidx.work.Data);
+  }
+
+  public abstract class WorkContinuation {
+    ctor public WorkContinuation();
+    method public static androidx.work.WorkContinuation combine(java.util.List<androidx.work.WorkContinuation!>);
+    method public abstract androidx.work.Operation enqueue();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfos();
+    method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosLiveData();
+    method public final androidx.work.WorkContinuation then(androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.WorkContinuation then(java.util.List<androidx.work.OneTimeWorkRequest!>);
+  }
+
+  public final class WorkInfo {
+    method public java.util.UUID getId();
+    method public androidx.work.Data getOutputData();
+    method public androidx.work.Data getProgress();
+    method @IntRange(from=0) public int getRunAttemptCount();
+    method public androidx.work.WorkInfo.State getState();
+    method public java.util.Set<java.lang.String!> getTags();
+  }
+
+  public enum WorkInfo.State {
+    method public boolean isFinished();
+    enum_constant public static final androidx.work.WorkInfo.State BLOCKED;
+    enum_constant public static final androidx.work.WorkInfo.State CANCELLED;
+    enum_constant public static final androidx.work.WorkInfo.State ENQUEUED;
+    enum_constant public static final androidx.work.WorkInfo.State FAILED;
+    enum_constant public static final androidx.work.WorkInfo.State RUNNING;
+    enum_constant public static final androidx.work.WorkInfo.State SUCCEEDED;
+  }
+
+  public abstract class WorkManager {
+    method public final androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+    method public final androidx.work.WorkContinuation beginWith(androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.WorkContinuation beginWith(java.util.List<androidx.work.OneTimeWorkRequest!>);
+    method public abstract androidx.work.Operation cancelAllWork();
+    method public abstract androidx.work.Operation cancelAllWorkByTag(String);
+    method public abstract androidx.work.Operation cancelUniqueWork(String);
+    method public abstract androidx.work.Operation cancelWorkById(java.util.UUID);
+    method public abstract android.app.PendingIntent createCancelPendingIntent(java.util.UUID);
+    method public final androidx.work.Operation enqueue(androidx.work.WorkRequest);
+    method public abstract androidx.work.Operation enqueue(java.util.List<? extends androidx.work.WorkRequest>);
+    method public abstract androidx.work.Operation enqueueUniquePeriodicWork(String, androidx.work.ExistingPeriodicWorkPolicy, androidx.work.PeriodicWorkRequest);
+    method public androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+    method @Deprecated public static androidx.work.WorkManager getInstance();
+    method public static androidx.work.WorkManager getInstance(android.content.Context);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Long!> getLastCancelAllTimeMillis();
+    method public abstract androidx.lifecycle.LiveData<java.lang.Long!> getLastCancelAllTimeMillisLiveData();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.WorkInfo!> getWorkInfoById(java.util.UUID);
+    method public abstract androidx.lifecycle.LiveData<androidx.work.WorkInfo!> getWorkInfoByIdLiveData(java.util.UUID);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosByTag(String);
+    method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosByTagLiveData(String);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosForUniqueWork(String);
+    method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosForUniqueWorkLiveData(String);
+    method public static void initialize(android.content.Context, androidx.work.Configuration);
+    method public abstract androidx.work.Operation pruneWork();
+  }
+
+  public abstract class WorkRequest {
+    method public java.util.UUID getId();
+    field public static final long DEFAULT_BACKOFF_DELAY_MILLIS = 30000L; // 0x7530L
+    field public static final long MAX_BACKOFF_MILLIS = 18000000L; // 0x112a880L
+    field public static final long MIN_BACKOFF_MILLIS = 10000L; // 0x2710L
+  }
+
+  public abstract static class WorkRequest.Builder<B extends androidx.work.WorkRequest.Builder<?, ?>, W extends androidx.work.WorkRequest> {
+    method public final B addTag(String);
+    method public final W build();
+    method public final B keepResultsForAtLeast(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public final B keepResultsForAtLeast(java.time.Duration);
+    method public final B setBackoffCriteria(androidx.work.BackoffPolicy, long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public final B setBackoffCriteria(androidx.work.BackoffPolicy, java.time.Duration);
+    method public final B setConstraints(androidx.work.Constraints);
+    method public B setInitialDelay(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public B setInitialDelay(java.time.Duration);
+    method public final B setInputData(androidx.work.Data);
+  }
+
+  public abstract class Worker extends androidx.work.ListenableWorker {
+    ctor @Keep public Worker(android.content.Context, androidx.work.WorkerParameters);
+    method @WorkerThread public abstract androidx.work.ListenableWorker.Result doWork();
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+  }
+
+  public abstract class WorkerFactory {
+    ctor public WorkerFactory();
+    method public abstract androidx.work.ListenableWorker? createWorker(android.content.Context, String, androidx.work.WorkerParameters);
+  }
+
+  public final class WorkerParameters {
+    method public java.util.UUID getId();
+    method public androidx.work.Data getInputData();
+    method @RequiresApi(28) public android.net.Network? getNetwork();
+    method @IntRange(from=0) public int getRunAttemptCount();
+    method public java.util.Set<java.lang.String!> getTags();
+    method @RequiresApi(24) public java.util.List<java.lang.String!> getTriggeredContentAuthorities();
+    method @RequiresApi(24) public java.util.List<android.net.Uri!> getTriggeredContentUris();
+  }
+
+}
+
diff --git a/work/workmanager/api/res-2.3.0-rc01.txt b/work/workmanager/api/res-2.3.0-rc01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager/api/res-2.3.0-rc01.txt