[go: nahoru, domu]

Created Constraints2 in preparation for removing IntPx

Bug: 148980115

Relnote: "Added Constraints2, a copy of Constraints that does
only supports Int connstraints values rather than IntPx. IntPx
will be removed and all integer constraints will be assumed to
be pixels like Android.

Added IntSize as well, which will eventually replace IntPxSize."

The next step will be to introduce a new layout modifier that
uses Constraints2 instead of Constraints and then slowly
migrate all uses of layout modifiers to using the new one.

This new Constraints offers the possibility of being an
inline class, though it will restrict the maximum sizes
available to be less than 256K.

Test: New tests
Change-Id: I4b43a8b15e346138d3bd40427791d89472782e38
diff --git a/ui/ui-core/api/0.1.0-dev10.txt b/ui/ui-core/api/0.1.0-dev10.txt
index 9702a17..4673571 100644
--- a/ui/ui-core/api/0.1.0-dev10.txt
+++ b/ui/ui-core/api/0.1.0-dev10.txt
@@ -160,6 +160,41 @@
     method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
   }
 
+  public final inline class Constraints2 {
+    ctor public Constraints2();
+    method public static long constructor-impl(internal long value);
+    method public static long copy-impl(long $this, int minWidth = this.minWidth, int maxWidth = this.maxWidth, int minHeight = this.minHeight, int maxHeight = this.maxHeight);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static boolean getHasBoundedHeight-impl(long $this);
+    method public static boolean getHasBoundedWidth-impl(long $this);
+    method public static int getMaxHeight-impl(long $this);
+    method public static int getMaxWidth-impl(long $this);
+    method public static int getMinHeight-impl(long $this);
+    method public static int getMinWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static String toString-impl(long p);
+    field public static final androidx.ui.core.Constraints2.Companion! Companion;
+    field public static final int Infinity = -1073741824; // 0xc0000000
+  }
+
+  public static final class Constraints2.Companion {
+    method public long fixed(int width, int height);
+    method public long fixedHeight(int height);
+    method public long fixedWidth(int width);
+  }
+
+  public final class Constraints2Kt {
+    method public static long Constraints2(@IntRange(from=0) int minWidth = 0, int maxWidth = -1073741824, @IntRange(from=0) int minHeight = 0, int maxHeight = -1073741824);
+    method public static long constrain-M1ogGms(long, long size);
+    method public static long enforce-uQjoOwo(long, long otherConstraints);
+    method public static boolean getHasFixedHeight-ybNdgAI(long);
+    method public static boolean getHasFixedWidth-ybNdgAI(long);
+    method public static boolean isZero-ybNdgAI(long);
+    method public static long offset-xOKVbDg(long, int horizontal = 0, int vertical = 0);
+    method public static boolean satisfiedBy-M1ogGms(long, long size);
+  }
+
   public final class ConstraintsKt {
     method public static androidx.ui.unit.IntPxSize constrain(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
     method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
diff --git a/ui/ui-core/api/current.txt b/ui/ui-core/api/current.txt
index 9702a17..4673571 100644
--- a/ui/ui-core/api/current.txt
+++ b/ui/ui-core/api/current.txt
@@ -160,6 +160,41 @@
     method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
   }
 
+  public final inline class Constraints2 {
+    ctor public Constraints2();
+    method public static long constructor-impl(internal long value);
+    method public static long copy-impl(long $this, int minWidth = this.minWidth, int maxWidth = this.maxWidth, int minHeight = this.minHeight, int maxHeight = this.maxHeight);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static boolean getHasBoundedHeight-impl(long $this);
+    method public static boolean getHasBoundedWidth-impl(long $this);
+    method public static int getMaxHeight-impl(long $this);
+    method public static int getMaxWidth-impl(long $this);
+    method public static int getMinHeight-impl(long $this);
+    method public static int getMinWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static String toString-impl(long p);
+    field public static final androidx.ui.core.Constraints2.Companion! Companion;
+    field public static final int Infinity = -1073741824; // 0xc0000000
+  }
+
+  public static final class Constraints2.Companion {
+    method public long fixed(int width, int height);
+    method public long fixedHeight(int height);
+    method public long fixedWidth(int width);
+  }
+
+  public final class Constraints2Kt {
+    method public static long Constraints2(@IntRange(from=0) int minWidth = 0, int maxWidth = -1073741824, @IntRange(from=0) int minHeight = 0, int maxHeight = -1073741824);
+    method public static long constrain-M1ogGms(long, long size);
+    method public static long enforce-uQjoOwo(long, long otherConstraints);
+    method public static boolean getHasFixedHeight-ybNdgAI(long);
+    method public static boolean getHasFixedWidth-ybNdgAI(long);
+    method public static boolean isZero-ybNdgAI(long);
+    method public static long offset-xOKVbDg(long, int horizontal = 0, int vertical = 0);
+    method public static boolean satisfiedBy-M1ogGms(long, long size);
+  }
+
   public final class ConstraintsKt {
     method public static androidx.ui.unit.IntPxSize constrain(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
     method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
diff --git a/ui/ui-core/api/public_plus_experimental_0.1.0-dev10.txt b/ui/ui-core/api/public_plus_experimental_0.1.0-dev10.txt
index 9702a17..4673571 100644
--- a/ui/ui-core/api/public_plus_experimental_0.1.0-dev10.txt
+++ b/ui/ui-core/api/public_plus_experimental_0.1.0-dev10.txt
@@ -160,6 +160,41 @@
     method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
   }
 
+  public final inline class Constraints2 {
+    ctor public Constraints2();
+    method public static long constructor-impl(internal long value);
+    method public static long copy-impl(long $this, int minWidth = this.minWidth, int maxWidth = this.maxWidth, int minHeight = this.minHeight, int maxHeight = this.maxHeight);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static boolean getHasBoundedHeight-impl(long $this);
+    method public static boolean getHasBoundedWidth-impl(long $this);
+    method public static int getMaxHeight-impl(long $this);
+    method public static int getMaxWidth-impl(long $this);
+    method public static int getMinHeight-impl(long $this);
+    method public static int getMinWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static String toString-impl(long p);
+    field public static final androidx.ui.core.Constraints2.Companion! Companion;
+    field public static final int Infinity = -1073741824; // 0xc0000000
+  }
+
+  public static final class Constraints2.Companion {
+    method public long fixed(int width, int height);
+    method public long fixedHeight(int height);
+    method public long fixedWidth(int width);
+  }
+
+  public final class Constraints2Kt {
+    method public static long Constraints2(@IntRange(from=0) int minWidth = 0, int maxWidth = -1073741824, @IntRange(from=0) int minHeight = 0, int maxHeight = -1073741824);
+    method public static long constrain-M1ogGms(long, long size);
+    method public static long enforce-uQjoOwo(long, long otherConstraints);
+    method public static boolean getHasFixedHeight-ybNdgAI(long);
+    method public static boolean getHasFixedWidth-ybNdgAI(long);
+    method public static boolean isZero-ybNdgAI(long);
+    method public static long offset-xOKVbDg(long, int horizontal = 0, int vertical = 0);
+    method public static boolean satisfiedBy-M1ogGms(long, long size);
+  }
+
   public final class ConstraintsKt {
     method public static androidx.ui.unit.IntPxSize constrain(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
     method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
diff --git a/ui/ui-core/api/public_plus_experimental_current.txt b/ui/ui-core/api/public_plus_experimental_current.txt
index 9702a17..4673571 100644
--- a/ui/ui-core/api/public_plus_experimental_current.txt
+++ b/ui/ui-core/api/public_plus_experimental_current.txt
@@ -160,6 +160,41 @@
     method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
   }
 
+  public final inline class Constraints2 {
+    ctor public Constraints2();
+    method public static long constructor-impl(internal long value);
+    method public static long copy-impl(long $this, int minWidth = this.minWidth, int maxWidth = this.maxWidth, int minHeight = this.minHeight, int maxHeight = this.maxHeight);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static boolean getHasBoundedHeight-impl(long $this);
+    method public static boolean getHasBoundedWidth-impl(long $this);
+    method public static int getMaxHeight-impl(long $this);
+    method public static int getMaxWidth-impl(long $this);
+    method public static int getMinHeight-impl(long $this);
+    method public static int getMinWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static String toString-impl(long p);
+    field public static final androidx.ui.core.Constraints2.Companion! Companion;
+    field public static final int Infinity = -1073741824; // 0xc0000000
+  }
+
+  public static final class Constraints2.Companion {
+    method public long fixed(int width, int height);
+    method public long fixedHeight(int height);
+    method public long fixedWidth(int width);
+  }
+
+  public final class Constraints2Kt {
+    method public static long Constraints2(@IntRange(from=0) int minWidth = 0, int maxWidth = -1073741824, @IntRange(from=0) int minHeight = 0, int maxHeight = -1073741824);
+    method public static long constrain-M1ogGms(long, long size);
+    method public static long enforce-uQjoOwo(long, long otherConstraints);
+    method public static boolean getHasFixedHeight-ybNdgAI(long);
+    method public static boolean getHasFixedWidth-ybNdgAI(long);
+    method public static boolean isZero-ybNdgAI(long);
+    method public static long offset-xOKVbDg(long, int horizontal = 0, int vertical = 0);
+    method public static boolean satisfiedBy-M1ogGms(long, long size);
+  }
+
   public final class ConstraintsKt {
     method public static androidx.ui.unit.IntPxSize constrain(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
     method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
diff --git a/ui/ui-core/api/restricted_0.1.0-dev10.txt b/ui/ui-core/api/restricted_0.1.0-dev10.txt
index 9702a17..4673571 100644
--- a/ui/ui-core/api/restricted_0.1.0-dev10.txt
+++ b/ui/ui-core/api/restricted_0.1.0-dev10.txt
@@ -160,6 +160,41 @@
     method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
   }
 
+  public final inline class Constraints2 {
+    ctor public Constraints2();
+    method public static long constructor-impl(internal long value);
+    method public static long copy-impl(long $this, int minWidth = this.minWidth, int maxWidth = this.maxWidth, int minHeight = this.minHeight, int maxHeight = this.maxHeight);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static boolean getHasBoundedHeight-impl(long $this);
+    method public static boolean getHasBoundedWidth-impl(long $this);
+    method public static int getMaxHeight-impl(long $this);
+    method public static int getMaxWidth-impl(long $this);
+    method public static int getMinHeight-impl(long $this);
+    method public static int getMinWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static String toString-impl(long p);
+    field public static final androidx.ui.core.Constraints2.Companion! Companion;
+    field public static final int Infinity = -1073741824; // 0xc0000000
+  }
+
+  public static final class Constraints2.Companion {
+    method public long fixed(int width, int height);
+    method public long fixedHeight(int height);
+    method public long fixedWidth(int width);
+  }
+
+  public final class Constraints2Kt {
+    method public static long Constraints2(@IntRange(from=0) int minWidth = 0, int maxWidth = -1073741824, @IntRange(from=0) int minHeight = 0, int maxHeight = -1073741824);
+    method public static long constrain-M1ogGms(long, long size);
+    method public static long enforce-uQjoOwo(long, long otherConstraints);
+    method public static boolean getHasFixedHeight-ybNdgAI(long);
+    method public static boolean getHasFixedWidth-ybNdgAI(long);
+    method public static boolean isZero-ybNdgAI(long);
+    method public static long offset-xOKVbDg(long, int horizontal = 0, int vertical = 0);
+    method public static boolean satisfiedBy-M1ogGms(long, long size);
+  }
+
   public final class ConstraintsKt {
     method public static androidx.ui.unit.IntPxSize constrain(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
     method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
diff --git a/ui/ui-core/api/restricted_current.txt b/ui/ui-core/api/restricted_current.txt
index 9702a17..4673571 100644
--- a/ui/ui-core/api/restricted_current.txt
+++ b/ui/ui-core/api/restricted_current.txt
@@ -160,6 +160,41 @@
     method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
   }
 
+  public final inline class Constraints2 {
+    ctor public Constraints2();
+    method public static long constructor-impl(internal long value);
+    method public static long copy-impl(long $this, int minWidth = this.minWidth, int maxWidth = this.maxWidth, int minHeight = this.minHeight, int maxHeight = this.maxHeight);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static boolean getHasBoundedHeight-impl(long $this);
+    method public static boolean getHasBoundedWidth-impl(long $this);
+    method public static int getMaxHeight-impl(long $this);
+    method public static int getMaxWidth-impl(long $this);
+    method public static int getMinHeight-impl(long $this);
+    method public static int getMinWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static String toString-impl(long p);
+    field public static final androidx.ui.core.Constraints2.Companion! Companion;
+    field public static final int Infinity = -1073741824; // 0xc0000000
+  }
+
+  public static final class Constraints2.Companion {
+    method public long fixed(int width, int height);
+    method public long fixedHeight(int height);
+    method public long fixedWidth(int width);
+  }
+
+  public final class Constraints2Kt {
+    method public static long Constraints2(@IntRange(from=0) int minWidth = 0, int maxWidth = -1073741824, @IntRange(from=0) int minHeight = 0, int maxHeight = -1073741824);
+    method public static long constrain-M1ogGms(long, long size);
+    method public static long enforce-uQjoOwo(long, long otherConstraints);
+    method public static boolean getHasFixedHeight-ybNdgAI(long);
+    method public static boolean getHasFixedWidth-ybNdgAI(long);
+    method public static boolean isZero-ybNdgAI(long);
+    method public static long offset-xOKVbDg(long, int horizontal = 0, int vertical = 0);
+    method public static boolean satisfiedBy-M1ogGms(long, long size);
+  }
+
   public final class ConstraintsKt {
     method public static androidx.ui.unit.IntPxSize constrain(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
     method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Constraints2.kt b/ui/ui-core/src/main/java/androidx/ui/core/Constraints2.kt
new file mode 100644
index 0000000..0515540
--- /dev/null
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Constraints2.kt
@@ -0,0 +1,460 @@
+/*
+ * 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.annotation.IntRange
+import androidx.compose.Immutable
+import androidx.ui.unit.IntSize
+import kotlin.math.max
+
+/**
+ * Immutable constraints used for measuring child Layouts or [LayoutModifier2]s. A parent layout
+ * can measure their children using the measure method on the corresponding [Measurable]s,
+ * method which takes the [Constraints2] the child has to follow. A measured child is then
+ * responsible to choose for themselves and return a size which satisfies the set of [Constraints2]
+ * received from their parent:
+ * - minWidth <= chosenWidth <= maxWidth
+ * - minHeight <= chosenHeight <= maxHeight
+ * The parent can then access the child chosen size on the resulting [Placeable]. The parent is
+ * responsible of defining a valid positioning of the children according to their sizes, so the
+ * parent needs to measure the children with appropriate [Constraints2], such that whatever valid
+ * sizes children choose, they can be laid out in a way that also respects the parent's incoming
+ * [Constraints2]. Note that different children can be measured with different [Constraints2].
+ * A child is allowed to choose a size that does not satisfy its constraints. However, when this
+ * happens, the parent will not read from the [Placeable] the real size of the child, but rather
+ * one that was coerced in the child's constraints; therefore, a parent can assume that its
+ * children will always respect the constraints in their layout algorithm. When this does not
+ * happen in reality, the position assigned to the child will be automatically offset to be centered
+ * on the space assigned by the parent under the assumption that constraints were respected.
+ * A set of [Constraints2] can have infinite maxWidth and/or maxHeight. This is a trick often
+ * used by parents to ask their children for their preferred size: unbounded constraints force
+ * children whose default behavior is to fill the available space (always size to
+ * maxWidth/maxHeight) to have an opinion about their preferred size. Most commonly, when measured
+ * with unbounded [Constraints2], these children will fallback to size themselves to wrap their
+ * content, instead of expanding to fill the available space (this is not always true
+ * as it depends on the child layout model, but is a common behavior for core layout components).
+ *
+ * [Constraints2] uses a [Long] to represent four values, [minWidth], [minHeight], [maxWidth],
+ * and [maxHeight]. The range of the values varies to allow for at most 256K in one dimension.
+ * There are four possible maximum ranges, 13 bits/18 bits, and 15 bits/16 bits for either width
+ * or height, depending on the needs. For example, a width could range up to 18 bits
+ * and the height up to 13 bits. Alternatively, the width could range up to 16 bits and the height
+ * up to 15 bits. The height and width requirements can be reversed, with a height of up to 18 bits
+ * and width of 13 bits or height of 16 bits and width of 15 bits. Any constraints exceeding
+ * this range will fail.
+ */
+@Immutable
+inline class Constraints2(
+    @PublishedApi internal val value: Long
+) {
+    /**
+     * Indicates how the bits are assigned. One of:
+     * * MinFocusWidth
+     * * MaxFocusWidth
+     * * MinFocusHeight
+     * * MaxFocusHeight
+     */
+    private val focusIndex
+        get() = (value and FocusMask).toInt()
+
+    /**
+     * The minimum width that the measurement can take.
+     */
+    val minWidth: Int
+        @IntRange(from = 0)
+        get() {
+            val mask = WidthMask[focusIndex]
+            return ((value shr 2).toInt() and mask)
+        }
+
+    /**
+     * The maximum width that the measurement can take. This will either be
+     * a positive value greater than or equal to [minWidth] or [Constraints2.Infinity].
+     */
+    val maxWidth: Int
+        get() {
+            val mask = WidthMask[focusIndex]
+            val width = ((value shr 33).toInt() and mask)
+            return if (width == 0) Infinity else width - 1
+        }
+
+    /**
+     * The minimum height that the measurement can take.
+     */
+    val minHeight: Int
+        @IntRange(from = 0)
+        get() {
+            val focus = focusIndex
+            val mask = HeightMask[focus]
+            val offset = MinHeightOffsets[focus]
+            return (value shr offset).toInt() and mask
+        }
+
+    /**
+     * The maximum height that the measurement can take. This will either be
+     * a positive value greater than or equal to [minHeight] or [Constraints2.Infinity].
+     */
+    val maxHeight: Int
+        get() {
+            val focus = focusIndex
+            val mask = HeightMask[focus]
+            val offset = MinHeightOffsets[focus] + 31
+            val height = (value shr offset).toInt() and mask
+            return if (height == 0) Infinity else height - 1
+        }
+
+    /**
+     * Whether or not the upper bound on the maximum width.
+     * @see hasBoundedHeight
+     */
+    val hasBoundedWidth: Boolean
+        get() {
+            val mask = WidthMask[focusIndex]
+            return ((value shr 33).toInt() and mask) != 0
+        }
+
+    /**
+     * Whether or not the upper bound on the maximum height.
+     * @see hasBoundedWidth
+     */
+    val hasBoundedHeight: Boolean
+        get() {
+            val focus = focusIndex
+            val mask = HeightMask[focus]
+            val offset = MinHeightOffsets[focus] + 31
+            return ((value shr offset).toInt() and mask) != 0
+        }
+
+    /**
+     * Copies the existing [Constraints2], replacing some of [minWidth], [minHeight], [maxWidth],
+     * or [maxHeight] as desired. [minWidth] and [minHeight] must be positive and
+     * [maxWidth] and [maxHeight] must be greater than or equal to [minWidth] and [minHeight],
+     * respectively, or [Infinity].
+     */
+    fun copy(
+        minWidth: Int = this.minWidth,
+        maxWidth: Int = this.maxWidth,
+        minHeight: Int = this.minHeight,
+        maxHeight: Int = this.maxHeight
+    ): Constraints2 {
+        require(minHeight >= 0 && minWidth >= 0) {
+            "minHeight($minHeight) and minWidth($minWidth) must be >= 0"
+        }
+        require(maxWidth > minWidth || maxWidth == Infinity) {
+            "maxWidth($maxWidth) must be greater than minWidth($minWidth)"
+        }
+        require(maxHeight > minHeight || maxHeight == Infinity) {
+            "maxHeight($maxHeight) must be greater than minHeight($minHeight)"
+        }
+        return createConstraints(minWidth, maxWidth, minHeight, maxHeight)
+    }
+
+    companion object {
+        // Infinity is set to a negative value in the middle of the Int range so that
+        // errors can be more easily detected. Adding or subtracting from [Infinity] will
+        // not easily roll over into a valid constraint value.
+        /**
+         * A value that [maxWidth] or [maxHeight] will be set to when the constraint should
+         * be considered infinite. [hasBoundedHeight] or [hasBoundedWidth] will be
+         * `true` when [maxHeight] or [maxWidth] is [Infinity], respectively.
+         */
+        const val Infinity = Integer.MIN_VALUE / 2
+
+        /**
+         * The bit distribution when the focus of the bits should be on the width, but only
+         * a minimal difference in focus.
+         *
+         * 16 bits assigned to width, 15 bits assigned to height.
+         */
+        private const val MinFocusWidth = 0x00L
+
+        /**
+         * The bit distribution when the focus of the bits should be on the width, and a
+         * maximal number of bits assigned to the width.
+         *
+         * 18 bits assigned to width, 13 bits assigned to height.
+         */
+        private const val MaxFocusWidth = 0x01L
+
+        /**
+         * The bit distribution when the focus of the bits should be on the height, but only
+         * a minimal difference in focus.
+         *
+         * 15 bits assigned to width, 16 bits assigned to height.
+         */
+        private const val MinFocusHeight = 0x02L
+
+        /**
+         * The bit distribution when the focus of the bits should be on the height, and a
+         * a maximal number of bits assigned to the height.
+         *
+         * 13 bits assigned to width, 18 bits assigned to height.
+         */
+        private const val MaxFocusHeight = 0x03L
+
+        /**
+         * The mask to retrieve the focus ([MinFocusWidth], [MaxFocusWidth],
+         * [MinFocusHeight], [MaxFocusHeight]).
+         */
+        private const val FocusMask = 0x03L
+
+        /**
+         * The number of bits used for the focused dimension when there is minimal focus.
+         */
+        private const val MinFocusBits = 16
+
+        /**
+         * The mask to use for the focused dimension when there is minimal focus.
+         */
+        private const val MinFocusMask = 0xFFFF // 64K (16 bits)
+
+        /**
+         * The number of bits used for the non-focused dimension when there is minimal focus.
+         */
+        private const val MinNonFocusBits = 15
+
+        /**
+         * The mask to use for the non-focused dimension when there is minimal focus.
+         */
+        private const val MinNonFocusMask = 0x7FFF // 32K (15 bits)
+
+        /**
+         * The number of bits to use for the focused dimension when there is maximal focus.
+         */
+        private const val MaxFocusBits = 18
+
+        /**
+         * The mask to use for the focused dimension when there is maximal focus.
+         */
+        private const val MaxFocusMask = 0x3FFFF // 256K (18 bits)
+
+        /**
+         * The number of bits to use for the non-focused dimension when there is maximal focus.
+         */
+        private const val MaxNonFocusBits = 13
+
+        /**
+         * The mask to use for the non-focused dimension when there is maximal focus.
+         */
+        private const val MaxNonFocusMask = 0x1FFF // 8K (13 bits)
+
+        /**
+         * Minimum Height shift offsets into Long value, indexed by FocusMask
+         * Max offsets are these + 31
+         * Width offsets are always either 2 (min) or 33 (max)
+         */
+        private val MinHeightOffsets = intArrayOf(
+            18, // MinFocusWidth: 2 + 16
+            20, // MaxFocusWidth: 2 + 18
+            17, // MinFocusHeight: 2 + 15
+            15 // MaxFocusHeight: 2 + 13
+        )
+
+        /**
+         * The mask to use for both minimum and maximum width.
+         */
+        private val WidthMask = intArrayOf(
+            MinFocusMask, // MinFocusWidth (16 bits)
+            MaxFocusMask, // MaxFocusWidth (18 bits)
+            MinNonFocusMask, // MinFocusHeight (15 bits)
+            MaxNonFocusMask // MaxFocusHeight (13 bits)
+        )
+
+        /**
+         * The mask to use for both minimum and maximum height.
+         */
+        private val HeightMask = intArrayOf(
+            MinNonFocusMask, // MinFocusWidth (15 bits)
+            MaxNonFocusMask, // MaxFocusWidth (13 bits)
+            MinFocusMask, // MinFocusHeight (16 bits)
+            MaxFocusMask // MaxFocusHeight (18 bits)
+        )
+
+        /**
+         * Creates constraints for fixed size in both dimensions.
+         */
+        fun fixed(width: Int, height: Int): Constraints2 {
+            require(width >= 0 && height >= 0) {
+                "width($width) and height($height) must be >= 0"
+            }
+            return createConstraints(width, width, height, height)
+        }
+
+        /**
+         * Creates constraints for fixed width and unspecified height.
+         */
+        fun fixedWidth(width: Int): Constraints2 {
+            require(width >= 0) {
+                "width($width) must be >= 0"
+            }
+            return createConstraints(
+                minWidth = width,
+                maxWidth = width,
+                minHeight = 0,
+                maxHeight = Infinity
+            )
+        }
+
+        /**
+         * Creates constraints for fixed height and unspecified width.
+         */
+        fun fixedHeight(height: Int): Constraints2 {
+            require(height >= 0) {
+                "height($height) must be >= 0"
+            }
+            return createConstraints(
+                minWidth = 0,
+                maxWidth = Infinity,
+                minHeight = height,
+                maxHeight = height
+            )
+        }
+
+        /**
+         * Creates a [Constraints2], only checking that the values fit in the packed Long.
+         */
+        internal fun createConstraints(
+            minWidth: Int,
+            maxWidth: Int,
+            minHeight: Int,
+            maxHeight: Int
+        ): Constraints2 {
+            val heightVal = max(minHeight, maxHeight) // maxHeight can be Infinity
+            val heightBits = bitsNeedForSize(heightVal)
+
+            val widthVal = max(minWidth, maxWidth) // maxWidth can be Infinity
+            val widthBits = bitsNeedForSize(widthVal)
+
+            if (widthBits + heightBits > 31) {
+                throw IllegalArgumentException(
+                    "Can't represent a width of $widthVal and height " +
+                            "of $heightVal in Constraints"
+                )
+            }
+
+            val focus = when (widthBits) {
+                MinNonFocusBits -> MinFocusHeight
+                MinFocusBits -> MinFocusWidth
+                MaxNonFocusBits -> MaxFocusHeight
+                MaxFocusBits -> MaxFocusWidth
+                else -> throw IllegalStateException("Should only have the provided constants.")
+            }
+
+            val maxWidthValue = if (maxWidth == Infinity) 0 else maxWidth + 1
+            val maxHeightValue = if (maxHeight == Infinity) 0 else maxHeight + 1
+
+            val minHeightOffset = MinHeightOffsets[focus.toInt()]
+            val maxHeightOffset = minHeightOffset + 31
+
+            val value = focus or
+                    (minWidth.toLong() shl 2) or
+                    (maxWidthValue.toLong() shl 33) or
+                    (minHeight.toLong() shl minHeightOffset) or
+                    (maxHeightValue.toLong() shl maxHeightOffset)
+            return Constraints2(value)
+        }
+
+        private fun bitsNeedForSize(size: Int): Int {
+            return when {
+                size < MaxNonFocusMask -> MaxNonFocusBits
+                size < MinNonFocusMask -> MinNonFocusBits
+                size < MinFocusMask -> MinFocusBits
+                size < MaxFocusMask -> MaxFocusBits
+                else -> throw IllegalArgumentException(
+                    "Can't represent a size of $size in " +
+                            "Constraints"
+                )
+            }
+        }
+    }
+}
+
+/**
+ * Create a [Constraints2]. [minWidth] and [minHeight] must be positive and
+ * [maxWidth] and [maxHeight] must be greater than or equal to [minWidth] and [minHeight],
+ * respectively, or [Infinity][Constraints2.Infinity].
+ */
+fun Constraints2(
+    @IntRange(from = 0)
+    minWidth: Int = 0,
+    maxWidth: Int = Constraints2.Infinity,
+    @IntRange(from = 0)
+    minHeight: Int = 0,
+    maxHeight: Int = Constraints2.Infinity
+): Constraints2 {
+    require(maxWidth >= minWidth || maxWidth == Constraints2.Infinity) {
+        "maxWidth($maxWidth) must be >= than minWidth($minWidth)"
+    }
+    require(maxHeight >= minHeight || maxHeight == Constraints2.Infinity) {
+        "maxHeight($maxHeight) must be >= than minHeight($minHeight)"
+    }
+    require(minWidth >= 0 && minHeight >= 0) {
+        "minWidth($minWidth) and minHeight($minHeight) must be >= 0"
+    }
+    return Constraints2.createConstraints(minWidth, maxWidth, minHeight, maxHeight)
+}
+
+/**
+ * Whether there is exactly one width value that satisfies the constraints.
+ */
+val Constraints2.hasFixedWidth get() = maxWidth == minWidth
+
+/**
+ * Whether there is exactly one height value that satisfies the constraints.
+ */
+val Constraints2.hasFixedHeight get() = maxHeight == minHeight
+
+/**
+ * Whether the area of a component respecting these constraints will definitely be 0.
+ * This is true when at least one of maxWidth and maxHeight are 0.
+ */
+val Constraints2.isZero get() = maxWidth == 0 || maxHeight == 0
+
+/**
+ * Returns the result of coercing the current constraints in a different set of constraints.
+ */
+fun Constraints2.enforce(otherConstraints: Constraints2) = Constraints2(
+    minWidth = minWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
+    maxWidth = maxWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
+    minHeight = minHeight.coerceIn(otherConstraints.minHeight, otherConstraints.maxHeight),
+    maxHeight = maxHeight.coerceIn(otherConstraints.minHeight, otherConstraints.maxHeight)
+)
+
+/**
+ * Takes a size and returns the closest size to it that satisfies the constraints.
+ */
+fun Constraints2.constrain(size: IntSize) = IntSize(
+    width = size.width.coerceIn(minWidth, maxWidth),
+    height = size.height.coerceIn(minHeight, maxHeight)
+)
+
+/**
+ * Takes a size and returns whether it satisfies the current constraints.
+ */
+fun Constraints2.satisfiedBy(size: IntSize) =
+    size.width in minWidth..maxWidth && size.height in minHeight..maxHeight
+
+/**
+ * Returns the Constraints obtained by offsetting the current instance with the given values.
+ */
+fun Constraints2.offset(horizontal: Int = 0, vertical: Int = 0) = Constraints2(
+    (minWidth + horizontal).coerceAtLeast(0),
+    (maxWidth + horizontal).coerceAtLeast(0),
+    (minHeight + vertical).coerceAtLeast(0),
+    (maxHeight + vertical).coerceAtLeast(0)
+)
diff --git a/ui/ui-core/src/test/java/androidx/ui/core/Constraints2Test.kt b/ui/ui-core/src/test/java/androidx/ui/core/Constraints2Test.kt
new file mode 100644
index 0000000..919b14e
--- /dev/null
+++ b/ui/ui-core/src/test/java/androidx/ui/core/Constraints2Test.kt
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.core
+
+import androidx.ui.unit.IntSize
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.IllegalArgumentException
+
+@RunWith(JUnit4::class)
+class Constraints2Test {
+
+    @Test
+    fun constructor() {
+        val defaultConstraints2 = Constraints2()
+        defaultConstraints2.assertEquals(0, Constraints2.Infinity, 0, Constraints2.Infinity)
+
+        val constraints = Constraints2(0, 1, 2, 3)
+        constraints.assertEquals(0, 1, 2, 3)
+
+        val fixedWidth = Constraints2.fixedWidth(5)
+        fixedWidth.assertEquals(5, 5, 0, Constraints2.Infinity)
+
+        val fixedHeight = Constraints2.fixedHeight(5)
+        fixedHeight.assertEquals(0, Constraints2.Infinity, 5, 5)
+
+        val fixed = Constraints2.fixed(5, 7)
+        fixed.assertEquals(5, 5, 7, 7)
+    }
+
+    @Test
+    fun retrieveSimpleValues() {
+        testConstraints() // Infinity max
+        testConstraints(0, 0, 0, 0)
+    }
+
+    @Test
+    fun retrieveValueMinFocusWidth() {
+        testConstraints(minWidth = 1, maxWidth = 64000, minHeight = 2, maxHeight = 32000)
+        testConstraints(minWidth = 64000, minHeight = 32000)
+        testConstraints(minWidth = 0xFFFE, minHeight = 0x7FFE)
+        testConstraints(maxWidth = 0xFFFE, maxHeight = 0x7FFE)
+    }
+
+    @Test
+    fun retrieveValueMinFocusHeight() {
+        testConstraints(minWidth = 1, maxWidth = 32000, minHeight = 2, maxHeight = 64000)
+        testConstraints(minWidth = 32000, maxWidth = 32001, minHeight = 64000, maxHeight = 64001)
+        testConstraints(minWidth = 32000, minHeight = 64000)
+        testConstraints(minWidth = 0x7FFE, minHeight = 0xFFFE)
+        testConstraints(maxWidth = 0x7FFE, maxHeight = 0xFFFE)
+    }
+
+    @Test
+    fun retrieveValueMaxFocusWidth() {
+        testConstraints(minWidth = 1, maxWidth = 250000, minHeight = 2, maxHeight = 8000)
+        testConstraints(minWidth = 250000, maxWidth = 250001, minHeight = 8000, maxHeight = 8001)
+        testConstraints(minWidth = 250000, minHeight = 8000)
+        testConstraints(minWidth = 0x3FFFE, minHeight = 0x1FFE)
+        testConstraints(maxWidth = 0x3FFFE, maxHeight = 0x1FFE)
+    }
+
+    @Test
+    fun retrieveValueMaxFocusHeight() {
+        testConstraints(minWidth = 1, maxWidth = 8000, minHeight = 2, maxHeight = 250000)
+        testConstraints(minWidth = 8000, maxWidth = 8001, minHeight = 250000, maxHeight = 250001)
+        testConstraints(minWidth = 8000, minHeight = 250000)
+        testConstraints(minWidth = 0x1FFE, minHeight = 0x3FFFE)
+        testConstraints(maxWidth = 0x1FFE, maxHeight = 0x3FFFE)
+    }
+
+    @Test
+    fun hasBoundedDimensions() {
+        val unbounded = Constraints2(3, Constraints2.Infinity, 3, Constraints2.Infinity)
+        assertFalse(unbounded.hasBoundedWidth)
+        assertFalse(unbounded.hasBoundedHeight)
+
+        val bounded = Constraints2(3, 5, 3, 5)
+        assertTrue(bounded.hasBoundedWidth)
+        assertTrue(bounded.hasBoundedHeight)
+    }
+
+    @Test
+    fun hasFixedDimensions() {
+        val untight = Constraints2(3, 4, 8, 9)
+        assertFalse(untight.hasFixedWidth)
+        assertFalse(untight.hasFixedHeight)
+
+        val tight = Constraints2(3, 3, 5, 5)
+        assertTrue(tight.hasFixedWidth)
+        assertTrue(tight.hasFixedHeight)
+    }
+
+    @Test
+    fun isZero() {
+        val nonZero = Constraints2(1, 2, 1, 2)
+        assertFalse(nonZero.isZero)
+
+        val zero = Constraints2(0, 0, 0, 0)
+        assertTrue(zero.isZero)
+
+        val zero12 = Constraints2(0, 0, 1, 2)
+        assertTrue(zero12.isZero)
+    }
+
+    @Test
+    fun enforce() {
+        val constraints = Constraints2(5, 10, 5, 10)
+        constraints.enforce(Constraints2(4, 11, 4, 11)).assertEquals(
+            5, 10, 5, 10
+        )
+        constraints.enforce(Constraints2(7, 9, 7, 9)).assertEquals(
+            7, 9, 7, 9
+        )
+        constraints.enforce(Constraints2(2, 3, 2, 3)).assertEquals(
+            3, 3, 3, 3
+        )
+        constraints.enforce(Constraints2(10, 11, 10, 11)).assertEquals(
+            10, 10, 10, 10
+        )
+    }
+
+    @Test
+    fun constrain() {
+        val constraints = Constraints2(2, 5, 2, 5)
+        assertEquals(IntSize(2, 2), constraints.constrain(IntSize(1, 1)))
+        assertEquals(IntSize(3, 3), constraints.constrain(IntSize(3, 3)))
+        assertEquals(IntSize(5, 5), constraints.constrain(IntSize(7, 7)))
+    }
+
+    @Test
+    fun satisfiedBy() {
+        val constraints = Constraints2(2, 5, 7, 9)
+        assertTrue(constraints.satisfiedBy(IntSize(4, 8)))
+        assertTrue(constraints.satisfiedBy(IntSize(2, 7)))
+        assertTrue(constraints.satisfiedBy(IntSize(5, 9)))
+        assertFalse(constraints.satisfiedBy(IntSize(1, 8)))
+        assertFalse(constraints.satisfiedBy(IntSize(7, 8)))
+        assertFalse(constraints.satisfiedBy(IntSize(4, 5)))
+        assertFalse(constraints.satisfiedBy(IntSize(4, 11)))
+    }
+
+    @Test
+    fun offset() {
+        val constraints = Constraints2(2, 2, 5, 5)
+        constraints.offset(horizontal = 2, vertical = 3).assertEquals(
+            4, 4, 8, 8
+        )
+        constraints.offset(horizontal = -7, vertical = -7).assertEquals(
+            0, 0, 0, 0
+        )
+    }
+
+    @Test
+    fun validity() {
+        assertInvalid(minWidth = Constraints2.Infinity)
+        assertInvalid(minHeight = Constraints2.Infinity)
+        assertInvalid(minWidth = 3, maxWidth = 2)
+        assertInvalid(minHeight = 3, maxHeight = 2)
+        assertInvalid(minWidth = -1)
+        assertInvalid(maxWidth = -1)
+        assertInvalid(minHeight = -1)
+        assertInvalid(maxHeight = -1)
+        assertInvalid(minWidth = 1000000)
+        assertInvalid(minHeight = 1000000)
+        assertInvalid(minWidth = 0x3FFFF)
+        assertInvalid(maxWidth = 0x3FFFF)
+        assertInvalid(minHeight = 0x3FFFF)
+        assertInvalid(maxHeight = 0x3FFFF)
+        assertInvalid(maxWidth = 0x1FFF, maxHeight = 0x3FFFE)
+        assertInvalid(maxWidth = 0x3FFFF, maxHeight = 0x1FFF)
+        assertInvalid(minWidth = 0x7FFE, minHeight = 0xFFFF)
+        assertInvalid(minWidth = 0x7FFF, minHeight = 0xFFFE)
+        assertInvalid(minWidth = 0xFFFE, minHeight = 0x7FFF)
+        assertInvalid(minWidth = 0xFFFF, minHeight = 0x7FFE)
+    }
+
+    private fun testConstraints(
+        minWidth: Int = 0,
+        maxWidth: Int = Constraints2.Infinity,
+        minHeight: Int = 0,
+        maxHeight: Int = Constraints2.Infinity
+    ) {
+        val constraints = Constraints2(
+            minWidth = minWidth,
+            minHeight = minHeight,
+            maxWidth = maxWidth,
+            maxHeight = maxHeight
+        )
+        assertEquals(minWidth, constraints.minWidth)
+        assertEquals(minHeight, constraints.minHeight)
+        assertEquals(maxWidth, constraints.maxWidth)
+        assertEquals(maxHeight, constraints.maxHeight)
+    }
+
+    private fun Constraints2.assertEquals(
+        minWidth: Int,
+        maxWidth: Int,
+        minHeight: Int,
+        maxHeight: Int
+    ) {
+        assertTrue(
+            this.minWidth == minWidth && this.maxWidth == maxWidth &&
+                    this.minHeight == minHeight && this.maxHeight == maxHeight
+        )
+    }
+
+    private fun assertInvalid(
+        minWidth: Int = 0,
+        maxWidth: Int = Constraints2.Infinity,
+        minHeight: Int = 0,
+        maxHeight: Int = Constraints2.Infinity
+    ) {
+        val constraints: Constraints2
+        try {
+            constraints = Constraints2(minWidth, maxWidth, minHeight, maxHeight)
+        } catch (_: IllegalArgumentException) {
+            return
+        }
+        fail("Invalid constraints $constraints are considered valid")
+    }
+}
diff --git a/ui/ui-unit/api/0.1.0-dev10.txt b/ui/ui-unit/api/0.1.0-dev10.txt
index 540e0ce..96414f2 100644
--- a/ui/ui-unit/api/0.1.0-dev10.txt
+++ b/ui/ui-unit/api/0.1.0-dev10.txt
@@ -312,6 +312,24 @@
     property public final inline androidx.ui.unit.IntPx width;
   }
 
+  public final inline class IntSize {
+    ctor public IntSize();
+    method public static long constructor-impl(internal long value);
+    method public static operator long div-impl(long $this, int other);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static int getHeight-impl(long $this);
+    method public static int getWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static operator long times-impl(long $this, int other);
+    method public static String toString-impl(long $this);
+  }
+
+  public final class IntSizeKt {
+    method public static long IntSize(int width, int height);
+    method public static operator long times-twsQOwo(int, long size);
+  }
+
   public final inline class Position {
     ctor public Position();
     method public static long constructor-impl(internal long value);
diff --git a/ui/ui-unit/api/current.txt b/ui/ui-unit/api/current.txt
index 540e0ce..96414f2 100644
--- a/ui/ui-unit/api/current.txt
+++ b/ui/ui-unit/api/current.txt
@@ -312,6 +312,24 @@
     property public final inline androidx.ui.unit.IntPx width;
   }
 
+  public final inline class IntSize {
+    ctor public IntSize();
+    method public static long constructor-impl(internal long value);
+    method public static operator long div-impl(long $this, int other);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static int getHeight-impl(long $this);
+    method public static int getWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static operator long times-impl(long $this, int other);
+    method public static String toString-impl(long $this);
+  }
+
+  public final class IntSizeKt {
+    method public static long IntSize(int width, int height);
+    method public static operator long times-twsQOwo(int, long size);
+  }
+
   public final inline class Position {
     ctor public Position();
     method public static long constructor-impl(internal long value);
diff --git a/ui/ui-unit/api/public_plus_experimental_0.1.0-dev10.txt b/ui/ui-unit/api/public_plus_experimental_0.1.0-dev10.txt
index 540e0ce..96414f2 100644
--- a/ui/ui-unit/api/public_plus_experimental_0.1.0-dev10.txt
+++ b/ui/ui-unit/api/public_plus_experimental_0.1.0-dev10.txt
@@ -312,6 +312,24 @@
     property public final inline androidx.ui.unit.IntPx width;
   }
 
+  public final inline class IntSize {
+    ctor public IntSize();
+    method public static long constructor-impl(internal long value);
+    method public static operator long div-impl(long $this, int other);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static int getHeight-impl(long $this);
+    method public static int getWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static operator long times-impl(long $this, int other);
+    method public static String toString-impl(long $this);
+  }
+
+  public final class IntSizeKt {
+    method public static long IntSize(int width, int height);
+    method public static operator long times-twsQOwo(int, long size);
+  }
+
   public final inline class Position {
     ctor public Position();
     method public static long constructor-impl(internal long value);
diff --git a/ui/ui-unit/api/public_plus_experimental_current.txt b/ui/ui-unit/api/public_plus_experimental_current.txt
index 540e0ce..96414f2 100644
--- a/ui/ui-unit/api/public_plus_experimental_current.txt
+++ b/ui/ui-unit/api/public_plus_experimental_current.txt
@@ -312,6 +312,24 @@
     property public final inline androidx.ui.unit.IntPx width;
   }
 
+  public final inline class IntSize {
+    ctor public IntSize();
+    method public static long constructor-impl(internal long value);
+    method public static operator long div-impl(long $this, int other);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static int getHeight-impl(long $this);
+    method public static int getWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static operator long times-impl(long $this, int other);
+    method public static String toString-impl(long $this);
+  }
+
+  public final class IntSizeKt {
+    method public static long IntSize(int width, int height);
+    method public static operator long times-twsQOwo(int, long size);
+  }
+
   public final inline class Position {
     ctor public Position();
     method public static long constructor-impl(internal long value);
diff --git a/ui/ui-unit/api/restricted_0.1.0-dev10.txt b/ui/ui-unit/api/restricted_0.1.0-dev10.txt
index 540e0ce..96414f2 100644
--- a/ui/ui-unit/api/restricted_0.1.0-dev10.txt
+++ b/ui/ui-unit/api/restricted_0.1.0-dev10.txt
@@ -312,6 +312,24 @@
     property public final inline androidx.ui.unit.IntPx width;
   }
 
+  public final inline class IntSize {
+    ctor public IntSize();
+    method public static long constructor-impl(internal long value);
+    method public static operator long div-impl(long $this, int other);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static int getHeight-impl(long $this);
+    method public static int getWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static operator long times-impl(long $this, int other);
+    method public static String toString-impl(long $this);
+  }
+
+  public final class IntSizeKt {
+    method public static long IntSize(int width, int height);
+    method public static operator long times-twsQOwo(int, long size);
+  }
+
   public final inline class Position {
     ctor public Position();
     method public static long constructor-impl(internal long value);
diff --git a/ui/ui-unit/api/restricted_current.txt b/ui/ui-unit/api/restricted_current.txt
index 540e0ce..96414f2 100644
--- a/ui/ui-unit/api/restricted_current.txt
+++ b/ui/ui-unit/api/restricted_current.txt
@@ -312,6 +312,24 @@
     property public final inline androidx.ui.unit.IntPx width;
   }
 
+  public final inline class IntSize {
+    ctor public IntSize();
+    method public static long constructor-impl(internal long value);
+    method public static operator long div-impl(long $this, int other);
+    method public static boolean equals-impl(long p, Object? p1);
+    method public static boolean equals-impl0(long p1, long p2);
+    method public static int getHeight-impl(long $this);
+    method public static int getWidth-impl(long $this);
+    method public static int hashCode-impl(long p);
+    method public static operator long times-impl(long $this, int other);
+    method public static String toString-impl(long $this);
+  }
+
+  public final class IntSizeKt {
+    method public static long IntSize(int width, int height);
+    method public static operator long times-twsQOwo(int, long size);
+  }
+
   public final inline class Position {
     ctor public Position();
     method public static long constructor-impl(internal long value);
diff --git a/ui/ui-unit/src/main/java/androidx/ui/unit/IntSize.kt b/ui/ui-unit/src/main/java/androidx/ui/unit/IntSize.kt
new file mode 100644
index 0000000..255672b5d
--- /dev/null
+++ b/ui/ui-unit/src/main/java/androidx/ui/unit/IntSize.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.ui.unit
+
+import androidx.compose.Immutable
+import androidx.ui.util.packInts
+import androidx.ui.util.unpackInt1
+import androidx.ui.util.unpackInt2
+
+/**
+ * A two-dimensional size class used for measuring in [Int] pixels.
+ */
+@Immutable
+inline class IntSize(@PublishedApi internal val value: Long) {
+    /**
+     * The horizontal aspect of the size in [Int] pixels.
+     */
+    val width: Int
+        get() = unpackInt1(value)
+
+    /**
+     * The vertical aspect of the size in [Int] pixels.
+     */
+    val height: Int
+        get() = unpackInt2(value)
+
+    /**
+     * Returns an IntSize scaled by multiplying [width] and [height] by [other]
+     */
+    operator fun times(other: Int): IntSize =
+        IntSize(width = width * other, height = height * other)
+
+    /**
+     * Returns an IntSize scaled by dividing [width] and [height] by [other]
+     */
+    operator fun div(other: Int): IntSize =
+        IntSize(width = width / other, height = height / other)
+
+    override fun toString(): String = "$width x $height"
+}
+
+/**
+ * Returns an [IntSize] with [size]'s [IntSize.width] and [IntSize.height]
+ * multiplied by [this].
+ */
+operator fun Int.times(size: IntSize) = size * this
+
+/**
+ * Constructs an [IntPxSize] from width and height [IntPx] values.
+ */
+fun IntSize(width: Int, height: Int): IntSize =
+    IntSize(packInts(width, height))
diff --git a/ui/ui-unit/src/test/java/androidx/ui/unit/IntSizeTest.kt b/ui/ui-unit/src/test/java/androidx/ui/unit/IntSizeTest.kt
new file mode 100644
index 0000000..ec27428
--- /dev/null
+++ b/ui/ui-unit/src/test/java/androidx/ui/unit/IntSizeTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.unit
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class IntSizeTest {
+    @Test
+    fun constructor() {
+        val size = IntSize(width = 5, height = 10)
+        assertEquals(5, size.width)
+        assertEquals(10, size.height)
+
+        val size2 = IntSize(width = Int.MAX_VALUE, height = Int.MIN_VALUE)
+        assertEquals(Int.MAX_VALUE, size2.width)
+        assertEquals(Int.MIN_VALUE, size2.height)
+    }
+
+    @Test
+    fun intSizeTimesInt() {
+        assertEquals(IntSize(10, 10), IntSize(2, 2) * 5)
+        assertEquals(IntSize(10, 10), 5 * IntSize(2, 2))
+    }
+
+    @Test
+    fun intSizeDivInt() {
+        assertEquals(IntSize(10, 10), IntSize(40, 40) / 4)
+    }
+}
\ No newline at end of file