Add dashed arcline proto and builder

Bug: 360312549
Change-Id: Ia8404e23fadc5630e268e3aeac91183f9361d004
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
index 540382b..b235c5a 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
@@ -736,6 +736,45 @@
   Shadow shadow = 2;
 }
 
+// A dashed arc line that can be placed in an Arc container. It is an arc line made up of arc line
+// segments separated by gaps.
+message DashedArcLine {
+  // The length of this line in degrees, including gaps. <setter> If not defined,
+  // defaults to 0.</setter>
+  //
+  // When using a dynamic value, make sure to specify the bounding constraints
+  // for the affected layout element through {@code
+  // setLayoutConstraintsForDynamicLength(AngularLayoutConstraint)} otherwise
+  // {@code build()} fails.
+  DegreesProp length = 1;
+
+  // The thickness of this line. <setter> If not defined, defaults to 0.</setter>
+  DpProp thickness = 2;
+
+  // The color of this line.
+  ColorProp color = 3;
+
+  // Modifiers for this element.
+  ArcModifiers modifiers = 4;
+
+  // The direction in which this line is drawn.<setter> If not set, defaults to
+  // ARC_DIRECTION_CLOCKWISE.</setter>
+  ArcDirectionProp arc_direction = 5;
+
+  // The dashed line pattern which describes how the arc line is segmented by gaps.
+  DashedLinePattern line_pattern = 6;
+}
+
+// A dashed line pattern which describes how the dashed arc line is segmented by gaps.
+message DashedLinePattern {
+  // The size in dp of the gap between the line segments.<setter> If not
+  // defined, defaults to 0.</setter>
+  DpProp gap_size = 1;
+
+  // The list of each gap's center location in degrees.
+  repeated DegreesProp gap_locations = 2;
+}
+
 // A simple spacer used to provide padding between adjacent elements in an Arc.
 message ArcSpacer {
   // The length of this spacer, in degrees. If not defined, defaults to 0.
@@ -837,6 +876,7 @@
     ArcLine line = 2;
     ArcSpacer spacer = 3;
     ArcAdapter adapter = 4;
+    DashedArcLine dashed_line = 5;
   }
 }
 
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
index 3be0537..d8ed179 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
@@ -3961,6 +3961,10 @@
                                 parentViewWrapper, element.getText(), nodePosId, pipelineMaker);
                 break;
 
+            case DASHED_LINE:
+                // TODO: b/360314390 - inflate a dashed arc here with WearDashedArcLineView
+                break;
+
             case INNER_NOT_SET:
                 break;
         }
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index 7ef6729..034cbf0 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -558,6 +558,42 @@
     method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp.Builder setValue(int);
   }
 
+  @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class LayoutElementBuilders.DashedArcLine implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcDirectionProp? getArcDirection();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicLength();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
+    method public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern? getLinePattern();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getThickness();
+  }
+
+  public static final class LayoutElementBuilders.DashedArcLine.Builder {
+    ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public LayoutElementBuilders.DashedArcLine.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine build();
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setArcDirection(androidx.wear.protolayout.LayoutElementBuilders.ArcDirectionProp);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setArcDirection(int);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLayoutConstraintsForDynamicLength(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLinePattern(androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setThickness(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class LayoutElementBuilders.DashedLinePattern {
+    method public java.util.List<androidx.wear.protolayout.DimensionBuilders.DegreesProp!> getGapLocations();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getGapSize();
+  }
+
+  public static final class LayoutElementBuilders.DashedLinePattern.Builder {
+    ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public LayoutElementBuilders.DashedLinePattern.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern build();
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapInterval(float);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapLocations(float...);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
   @SuppressCompatibility @androidx.wear.protolayout.expression.ExperimentalProtoLayoutExtensionApi @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class LayoutElementBuilders.ExtensionLayoutElement implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
     method public String getExtensionId();
     method public androidx.wear.protolayout.DimensionBuilders.ExtensionDimension? getHeight();
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index 7ef6729..034cbf0 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -558,6 +558,42 @@
     method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.ContentScaleModeProp.Builder setValue(int);
   }
 
+  @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class LayoutElementBuilders.DashedArcLine implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcDirectionProp? getArcDirection();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicLength();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
+    method public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern? getLinePattern();
+    method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getThickness();
+  }
+
+  public static final class LayoutElementBuilders.DashedArcLine.Builder {
+    ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public LayoutElementBuilders.DashedArcLine.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine build();
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setArcDirection(androidx.wear.protolayout.LayoutElementBuilders.ArcDirectionProp);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setArcDirection(int);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLayoutConstraintsForDynamicLength(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setLinePattern(androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine.Builder setThickness(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class LayoutElementBuilders.DashedLinePattern {
+    method public java.util.List<androidx.wear.protolayout.DimensionBuilders.DegreesProp!> getGapLocations();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getGapSize();
+  }
+
+  public static final class LayoutElementBuilders.DashedLinePattern.Builder {
+    ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public LayoutElementBuilders.DashedLinePattern.Builder();
+    method public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern build();
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapInterval(float);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapLocations(float...);
+    method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern.Builder setGapSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
   @SuppressCompatibility @androidx.wear.protolayout.expression.ExperimentalProtoLayoutExtensionApi @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class LayoutElementBuilders.ExtensionLayoutElement implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
     method public String getExtensionId();
     method public androidx.wear.protolayout.DimensionBuilders.ExtensionDimension? getHeight();
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
index 7c4af90..8fbf8f0 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
@@ -16,6 +16,9 @@
 
 package androidx.wear.protolayout;
 
+import static androidx.annotation.Dimension.DP;
+import static androidx.wear.protolayout.DimensionBuilders.degrees;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
 import static androidx.wear.protolayout.DimensionBuilders.sp;
 import static androidx.wear.protolayout.expression.Preconditions.checkNotNull;
 
@@ -5170,6 +5173,457 @@
         }
     }
 
+    /**
+     * A dashed arc line that can be placed in an {@link Arc} container. It is an arc line made up
+     * of arc line segments separated by gaps.
+     */
+    @RequiresSchemaVersion(major = 1, minor = 500)
+    public static final class DashedArcLine implements ArcLayoutElement {
+        private final LayoutElementProto.DashedArcLine mImpl;
+        @Nullable private final Fingerprint mFingerprint;
+
+        DashedArcLine(LayoutElementProto.DashedArcLine impl, @Nullable Fingerprint fingerprint) {
+            this.mImpl = impl;
+            this.mFingerprint = fingerprint;
+        }
+
+        /**
+         * Gets the length of this line in degrees, including gaps.
+         *
+         * <p>When using a dynamic value, make sure to specify the bounding constraints for the
+         * affected layout element through {@code setLayoutConstraintsForDynamicLength
+         * (AngularLayoutConstraint)}, otherwise {@code build()} fails.
+         */
+        @Nullable
+        public DegreesProp getLength() {
+            if (mImpl.hasLength()) {
+                return DegreesProp.fromProto(mImpl.getLength());
+            } else {
+                return null;
+            }
+        }
+
+        /** Gets the thickness of this line. */
+        @Dimension(unit = DP)
+        public float getThickness() {
+            if (mImpl.hasThickness()) {
+                return DpProp.fromProto(mImpl.getThickness()).getValue();
+            } else {
+                return 0;
+            }
+        }
+
+        /** Gets the color of this line. */
+        @Nullable
+        public ColorProp getColor() {
+            if (mImpl.hasColor()) {
+                return ColorProp.fromProto(mImpl.getColor());
+            } else {
+                return null;
+            }
+        }
+
+        /** Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element. */
+        @Nullable
+        public ArcModifiers getModifiers() {
+            if (mImpl.hasModifiers()) {
+                return ArcModifiers.fromProto(mImpl.getModifiers());
+            } else {
+                return null;
+            }
+        }
+
+        /** Gets the direction in which this line is drawn. */
+        @Nullable
+        public ArcDirectionProp getArcDirection() {
+            if (mImpl.hasArcDirection()) {
+                return ArcDirectionProp.fromProto(mImpl.getArcDirection());
+            } else {
+                return null;
+            }
+        }
+
+        /** Gets the dashed line pattern which describes how the arc line is segmented by gaps. */
+        @Nullable
+        public DashedLinePattern getLinePattern() {
+            if (mImpl.hasLinePattern()) {
+                return DashedLinePattern.fromProto(mImpl.getLinePattern());
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Gets the bounding constraints for the layout affected by the dynamic value from {@link
+         * #getLength()}.
+         */
+        @Nullable
+        public AngularLayoutConstraint getLayoutConstraintsForDynamicLength() {
+            if (mImpl.hasLength()) {
+                return AngularLayoutConstraint.fromProto(mImpl.getLength());
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Nullable
+        public Fingerprint getFingerprint() {
+            return mFingerprint;
+        }
+
+        /** Creates a new wrapper instance from the proto. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public static DashedArcLine fromProto(
+                @NonNull LayoutElementProto.DashedArcLine proto,
+                @Nullable Fingerprint fingerprint) {
+            return new DashedArcLine(proto, fingerprint);
+        }
+
+        @NonNull
+        static DashedArcLine fromProto(@NonNull LayoutElementProto.DashedArcLine proto) {
+            return fromProto(proto, null);
+        }
+
+        /** Returns the internal proto instance. */
+        @NonNull
+        LayoutElementProto.DashedArcLine toProto() {
+            return mImpl;
+        }
+
+        @Override
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public LayoutElementProto.ArcLayoutElement toArcLayoutElementProto() {
+            return LayoutElementProto.ArcLayoutElement.newBuilder().setDashedLine(mImpl).build();
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "DashedArcLine{"
+                    + "length="
+                    + getLength()
+                    + ", thickness="
+                    + getThickness()
+                    + ", color="
+                    + getColor()
+                    + ", modifiers="
+                    + getModifiers()
+                    + ", arcDirection="
+                    + getArcDirection()
+                    + ", linePattern="
+                    + getLinePattern()
+                    + "}";
+        }
+
+        /** Builder for {@link DashedArcLine}. */
+        @SuppressWarnings("HiddenSuperclass")
+        public static final class Builder implements ArcLayoutElement.Builder {
+            private final LayoutElementProto.DashedArcLine.Builder mImpl =
+                    LayoutElementProto.DashedArcLine.newBuilder();
+            private final Fingerprint mFingerprint = new Fingerprint(-1152963772);
+
+            /** Creates an instance of {@link Builder}. */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            public Builder() {}
+
+            /**
+             * Sets the length of this line, in degrees. If not defined, defaults to 0.
+             *
+             * <p>When using a dynamic value, make sure to specify the bounding constraints for the
+             * affected layout element through {@code setLayoutConstraintsForDynamicLength
+             * (AngularLayoutConstraint)} otherwise {@code build()} fails.
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            public Builder setLength(@NonNull DegreesProp length) {
+                mImpl.mergeLength(length.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        1, checkNotNull(length.getFingerprint()).aggregateValueAsInt());
+                return this;
+            }
+
+            /**
+             * Sets the thickness of this line. If not defined, defaults to 0.
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            public Builder setThickness(@Dimension(unit = DP) float thickness) {
+                DpProp thicknessProp = dp(thickness);
+                mImpl.setThickness(thicknessProp.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        2, checkNotNull(thicknessProp.getFingerprint()).aggregateValueAsInt());
+                return this;
+            }
+
+            /**
+             * Sets the color of this line.
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            public Builder setColor(@NonNull ColorProp color) {
+                mImpl.setColor(color.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        3, checkNotNull(color.getFingerprint()).aggregateValueAsInt());
+                return this;
+            }
+
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            public Builder setModifiers(@NonNull ArcModifiers modifiers) {
+                mImpl.setModifiers(modifiers.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        4, checkNotNull(modifiers.getFingerprint()).aggregateValueAsInt());
+                return this;
+            }
+
+            /**
+             * Sets the direction in which this line is drawn. If not set, defaults to
+             * ARC_DIRECTION_CLOCKWISE.
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            public Builder setArcDirection(@NonNull ArcDirectionProp arcDirection) {
+                mImpl.setArcDirection(arcDirection.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        5, checkNotNull(arcDirection.getFingerprint()).aggregateValueAsInt());
+                return this;
+            }
+
+            /**
+             * Sets the direction in which this line is drawn. If not set, defaults to
+             * ARC_DIRECTION_CLOCKWISE.
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            public Builder setArcDirection(@ArcDirection int arcDirection) {
+                return setArcDirection(
+                        new ArcDirectionProp.Builder().setValue(arcDirection).build());
+            }
+
+            /**
+             * Sets the dashed line pattern which describes how the arc line is segmented by gaps.
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            public Builder setLinePattern(@NonNull DashedLinePattern linePattern) {
+                mImpl.setLinePattern(linePattern.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        6, checkNotNull(linePattern.getFingerprint()).aggregateValueAsInt());
+                return this;
+            }
+            /**
+             * Sets the bounding constraints for the layout affected by the dynamic value from
+             * {@link #setLength(DegreesProp)}.
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            public Builder setLayoutConstraintsForDynamicLength(
+                    @NonNull AngularLayoutConstraint angularLayoutConstraint) {
+                mImpl.mergeLength(angularLayoutConstraint.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        1,
+                        checkNotNull(angularLayoutConstraint.getFingerprint())
+                                .aggregateValueAsInt());
+                return this;
+            }
+
+            /** Builds an instance with the values accumulated in this Builder. */
+            @SuppressLint("ProtoLayoutMinSchema")
+            @Override
+            @NonNull
+            public DashedArcLine build() {
+                DimensionProto.DegreesProp length = mImpl.getLength();
+                if (length.hasDynamicValue() && !length.hasValueForLayout()) {
+                    throw new IllegalStateException(
+                            "length with dynamic value requires "
+                                    + "layoutConstraintsForDynamicLength to be present.");
+                }
+
+                return new DashedArcLine(mImpl.build(), mFingerprint);
+            }
+        }
+    }
+
+    /** A dashed line pattern which describes how the dashed arc line is segmented by gaps. */
+    @RequiresSchemaVersion(major = 1, minor = 500)
+    public static final class DashedLinePattern {
+        private final LayoutElementProto.DashedLinePattern mImpl;
+        @Nullable private final Fingerprint mFingerprint;
+
+        DashedLinePattern(
+                LayoutElementProto.DashedLinePattern impl, @Nullable Fingerprint fingerprint) {
+            this.mImpl = impl;
+            this.mFingerprint = fingerprint;
+        }
+
+        /** Gets the size in dp of the gap between the arc line segments. */
+        @Nullable
+        public DpProp getGapSize() {
+            if (mImpl.hasGapSize()) {
+                return DpProp.fromProto(mImpl.getGapSize());
+            } else {
+                return null;
+            }
+        }
+
+        /** Gets the list of each gap's center location in degrees. */
+        @NonNull
+        public List<DegreesProp> getGapLocations() {
+            List<DegreesProp> list = new ArrayList<>();
+            for (DimensionProto.DegreesProp item : mImpl.getGapLocationsList()) {
+                list.add(DegreesProp.fromProto(item));
+            }
+            return Collections.unmodifiableList(list);
+        }
+
+        /** Get the fingerprint for this object, or null if unknown. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Nullable
+        public Fingerprint getFingerprint() {
+            return mFingerprint;
+        }
+
+        /** Creates a new wrapper instance from the proto. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public static DashedLinePattern fromProto(
+                @NonNull LayoutElementProto.DashedLinePattern proto,
+                @Nullable Fingerprint fingerprint) {
+            return new DashedLinePattern(proto, fingerprint);
+        }
+
+        @NonNull
+        static DashedLinePattern fromProto(@NonNull LayoutElementProto.DashedLinePattern proto) {
+            return fromProto(proto, null);
+        }
+
+        /** Returns the internal proto instance. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public LayoutElementProto.DashedLinePattern toProto() {
+            return mImpl;
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "DashedLinePattern{"
+                    + "gapSize="
+                    + getGapSize()
+                    + ", gapLocations="
+                    + getGapLocations()
+                    + "}";
+        }
+
+        /** Builder for {@link DashedLinePattern} */
+        public static final class Builder {
+            private final LayoutElementProto.DashedLinePattern.Builder mImpl =
+                    LayoutElementProto.DashedLinePattern.newBuilder();
+            private final Fingerprint mFingerprint = new Fingerprint(1050989205);
+
+            /** Creates an instance of {@link Builder}. */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            public Builder() {}
+
+            /**
+             * Sets the size in dp of the gap between the segments. If not defined, defaults to 0.
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            public Builder setGapSize(@Dimension(unit = DP) float gapSizeInDp) {
+                DpProp gapSizeProp = dp(gapSizeInDp);
+                mImpl.setGapSize(gapSizeProp.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        1, checkNotNull(gapSizeProp.getFingerprint()).aggregateValueAsInt());
+                return this;
+            }
+
+            /**
+             * Adds one item to the list of each gap's center location in degrees.
+             *
+             * <p>Note that this field only supports static values.
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            private Builder addGapLocation(@NonNull DegreesProp gapLocation) {
+                if (gapLocation.getDynamicValue() != null) {
+                    throw new IllegalArgumentException(
+                            "DashedLinePattern.Builder.addGapLocation doesn't support dynamic "
+                                    + "values.");
+                }
+                mImpl.addGapLocations(gapLocation.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        2, checkNotNull(gapLocation.getFingerprint()).aggregateValueAsInt());
+                return this;
+            }
+
+            /**
+             * Sets the list of each gap's center location in degrees.
+             *
+             * <p>The interval between any two locations could not be shorter than thickness plus
+             * gap size.
+             *
+             * <p>Note that calling this method will invalidate the previous call of {@link
+             * #setGapInterval}
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            public Builder setGapLocations(@NonNull float... gapLocationsInDegrees) {
+                mImpl.clearGapLocations();
+
+                for (float gapLocation: gapLocationsInDegrees) {
+                    addGapLocation(degrees(gapLocation));
+                }
+
+                return this;
+            }
+
+            /**
+             * Sets the interval length in degrees between two consecutive gap center locations. The
+             * arc line will have arc line segments with equal length.
+             *
+             * <p>The interval could not be shorter than thickness plus gap size.
+             *
+             * <p>Note that calling this method will remove all the gap locations set previously
+             * with {@link #setGapLocations}
+             */
+            @RequiresSchemaVersion(major = 1, minor = 500)
+            @NonNull
+            @SuppressLint("MissingGetterMatchingBuilder")
+            public Builder setGapInterval(float gapIntervalInDegrees) {
+                mImpl.clearGapLocations();
+
+                float gapLocation = gapIntervalInDegrees;
+                while (gapLocation <= 360F) {
+                    addGapLocation(degrees(gapLocation));
+                    gapLocation += gapIntervalInDegrees;
+                }
+
+                return this;
+            }
+
+            private static final int GAP_COUNTS_LIMIT = 100;
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public DashedLinePattern build() {
+                if (mImpl.getGapLocationsList().size() > GAP_COUNTS_LIMIT) {
+                    throw new IllegalArgumentException(
+                            "Number of gaps can't be larger than " + GAP_COUNTS_LIMIT + ".");
+                }
+                return new DashedLinePattern(mImpl.build(), mFingerprint);
+            }
+        }
+    }
+
+
     /** A simple spacer used to provide padding between adjacent elements in an {@link Arc}. */
     @RequiresSchemaVersion(major = 1, minor = 0)
     public static final class ArcSpacer implements ArcLayoutElement {
@@ -5871,6 +6325,9 @@
         if (proto.hasAdapter()) {
             return ArcAdapter.fromProto(proto.getAdapter(), fingerprint);
         }
+        if (proto.hasDashedLine()) {
+            return DashedArcLine.fromProto(proto.getDashedLine(), fingerprint);
+        }
         throw new IllegalStateException("Proto was not a recognised instance of ArcLayoutElement");
     }
 
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
index 1653411..5605c8b 100644
--- a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
@@ -21,6 +21,8 @@
 import static androidx.wear.protolayout.DimensionBuilders.expand;
 import static androidx.wear.protolayout.DimensionBuilders.sp;
 import static androidx.wear.protolayout.DimensionBuilders.weight;
+import static androidx.wear.protolayout.LayoutElementBuilders.ARC_DIRECTION_COUNTER_CLOCKWISE;
+import static androidx.wear.protolayout.LayoutElementBuilders.ARC_DIRECTION_NORMAL;
 import static androidx.wear.protolayout.LayoutElementBuilders.FontStyle.ROBOTO_FLEX_FONT;
 import static androidx.wear.protolayout.LayoutElementBuilders.TABULAR_OPTION_TAG;
 import static androidx.wear.protolayout.LayoutElementBuilders.WEIGHT_AXIS_TAG;
@@ -35,6 +37,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.wear.protolayout.expression.AppDataKey;
 import androidx.wear.protolayout.expression.DynamicBuilders;
+import androidx.wear.protolayout.LayoutElementBuilders.DashedArcLine;
+import androidx.wear.protolayout.LayoutElementBuilders.DashedLinePattern;
+import androidx.wear.protolayout.proto.ColorProto;
 import androidx.wear.protolayout.proto.DimensionProto;
 import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.protolayout.proto.TypesProto;
@@ -467,7 +472,7 @@
 
     @Test
     public void testArcs_withSetDirection_correctlySetsValues() {
-        int arcLineDirection = LayoutElementBuilders.ARC_DIRECTION_COUNTER_CLOCKWISE;
+        int arcLineDirection = ARC_DIRECTION_COUNTER_CLOCKWISE;
         int arcTextDirection = LayoutElementBuilders.ARC_DIRECTION_NORMAL;
         int arcDirection = LayoutElementBuilders.ARC_DIRECTION_CLOCKWISE;
 
@@ -712,4 +717,113 @@
         assertThat(fontStyleProto.getPreferredFontFamilies(0)).isEqualTo(expectedFontFamily);
         assertThat(fontStyleProto.getPreferredFontFamilies(1)).isEqualTo(fallbackFontFamily);
     }
+
+    @Test
+    public void dashedArcLine_length() {
+        DashedArcLine dashedArcLine =
+                new DashedArcLine.Builder()
+                        .setLength(DEGREES_PROP)
+                        .setLayoutConstraintsForDynamicLength(DEGREES_PROP_CONSTRAINT)
+                        .build();
+
+        DimensionProto.DegreesProp lengthProto = dashedArcLine.toProto().getLength();
+
+        assertThat(lengthProto.getValue()).isEqualTo(DEGREES_PROP.getValue());
+        assertThat(lengthProto.getDynamicValue().getStateSource().getSourceKey())
+                .isEqualTo(STATE_KEY);
+        assertThat(lengthProto.getValueForLayout()).isEqualTo(DEGREES_PROP_CONSTRAINT.getValue());
+        assertThat(lengthProto.getAngularAlignmentForLayoutValue())
+                .isEqualTo(DEGREES_PROP_CONSTRAINT.getAngularAlignment());
+    }
+
+    @Test
+    public void dashedArcLine_length_withoutLayoutConstraint_throws() {
+        assertThrows(
+                IllegalStateException.class,
+                () -> new DashedArcLine.Builder().setLength(DEGREES_PROP).build());
+    }
+
+    @Test
+    public void dashedArcLine_thickness() {
+        float thickness = 5F;
+        DashedArcLine dashedArcLine =
+                new DashedArcLine.Builder()
+                        .setThickness(thickness)
+                        .build();
+
+        assertThat(dashedArcLine.toProto().getThickness().getValue())
+                .isEqualTo(thickness);
+    }
+
+    @Test
+    public void dashedArcLine_color() {
+        String stateKey = "color-key";
+        ColorBuilders.ColorProp color =
+                new ColorBuilders.ColorProp.Builder(Color.BLUE)
+                        .setDynamicValue(DynamicBuilders.DynamicColor.from(
+                                new AppDataKey<>(stateKey)
+                        )).build();
+        DashedArcLine dashedArcLine =
+                new DashedArcLine.Builder()
+                        .setColor(color)
+                        .build();
+
+        ColorProto.ColorProp colorProto = dashedArcLine.toProto().getColor();
+        assertThat(colorProto.getArgb()).isEqualTo(Color.BLUE);
+        assertThat(colorProto.getDynamicValue().getStateSource().getSourceKey())
+                .isEqualTo(stateKey);
+    }
+
+    @Test
+    public void dashedArcLine_arcDirection() {
+        DashedArcLine dashedArcLine1 =
+                new DashedArcLine.Builder().build();
+        DashedArcLine dashedArcLine2 =
+                new DashedArcLine.Builder()
+                        .setArcDirection(ARC_DIRECTION_COUNTER_CLOCKWISE)
+                        .build();
+
+        assertThat(dashedArcLine1.toProto().getArcDirection().getValue().getNumber())
+                .isEqualTo(ARC_DIRECTION_NORMAL);
+        assertThat(dashedArcLine2.toProto().getArcDirection().getValue().getNumber())
+                .isEqualTo(ARC_DIRECTION_COUNTER_CLOCKWISE);
+    }
+
+    @Test
+    public void dashedArcLine_brushWithEqualSegments() {
+        DashedArcLine dashedArcLine =
+                new DashedArcLine.Builder()
+                        .setLinePattern(
+                                new DashedLinePattern.Builder()
+                                        .setGapSize(4.5F)
+                                        .setGapInterval(111)
+                                        .build())
+                        .build();
+
+        LayoutElementProto.DashedLinePattern brush = dashedArcLine.getLinePattern().toProto();
+        assertThat(brush.getGapSize().getValue()).isEqualTo(4.5F);
+        List<DimensionProto.DegreesProp> gapLocations =brush.getGapLocationsList();
+        assertThat(gapLocations.get(0).getValue()).isEqualTo(111F);
+        assertThat(gapLocations.get(1).getValue()).isEqualTo(222F);
+        assertThat(gapLocations.get(2).getValue()).isEqualTo(333F);
+    }
+
+    @Test
+    public void dashedArcLine_brushWithNonEqualSegments() {
+        DashedArcLine dashedArcLine =
+                new DashedArcLine.Builder()
+                        .setLinePattern(
+                                new DashedLinePattern.Builder()
+                                        .setGapSize(4.5F)
+                                        .setGapLocations(66F, 111F, 321F, 212F).build())
+                        .build();
+
+        LayoutElementProto.DashedLinePattern brush = dashedArcLine.getLinePattern().toProto();
+        assertThat(brush.getGapSize().getValue()).isEqualTo(4.5F);
+        List<DimensionProto.DegreesProp> gapLocations =brush.getGapLocationsList();
+        assertThat(gapLocations.get(0).getValue()).isEqualTo(66F);
+        assertThat(gapLocations.get(1).getValue()).isEqualTo(111F);
+        assertThat(gapLocations.get(2).getValue()).isEqualTo(321F);
+        assertThat(gapLocations.get(3).getValue()).isEqualTo(212F);
+    }
 }