Add StrokeCap Builders.
RelNote: Add StrokeCap support for ArcLine.
Bug: b/274752191
Test: Unit tests added.
Change-Id: I94951c20ab62491fa3d4eafdc119b80a66dc5431
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
index fe23db4..4d0a9d1 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
@@ -558,6 +558,10 @@
// Begin and end contours with a semi-circle extension. The extension size is
// proportional to the thickness on the path.
STROKE_CAP_ROUND = 2;
+
+ // Begin and end contours with a half square extension. The extension size is
+ // proportional to the thickness of the path.
+ STROKE_CAP_SQUARE = 3;
}
// An extensible StrokeCap property.
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 625a0d7..c7139bb 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
@@ -2604,6 +2604,9 @@
case STROKE_CAP_ROUND:
lineView.setStrokeCap(Cap.ROUND);
break;
+ case STROKE_CAP_SQUARE:
+ lineView.setStrokeCap(Cap.SQUARE);
+ break;
case UNRECOGNIZED:
case STROKE_CAP_UNDEFINED:
Log.w(TAG, "Undefined StrokeCap value.");
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index 69c5e47..208a7d4 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -333,6 +333,10 @@
field public static final int SPAN_VERTICAL_ALIGN_BOTTOM = 1; // 0x1
field public static final int SPAN_VERTICAL_ALIGN_TEXT_BASELINE = 2; // 0x2
field public static final int SPAN_VERTICAL_ALIGN_UNDEFINED = 0; // 0x0
+ field public static final int STROKE_CAP_BUTT = 1; // 0x1
+ field public static final int STROKE_CAP_ROUND = 2; // 0x2
+ field public static final int STROKE_CAP_SQUARE = 3; // 0x3
+ field public static final int STROKE_CAP_UNDEFINED = 0; // 0x0
field public static final int TEXT_ALIGN_CENTER = 2; // 0x2
field public static final int TEXT_ALIGN_END = 3; // 0x3
field public static final int TEXT_ALIGN_START = 1; // 0x1
@@ -403,6 +407,7 @@
method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicLength();
method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+ method public androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp? getStrokeCap();
method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
}
@@ -413,6 +418,8 @@
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLayoutConstraintsForDynamicLength(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+ method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setStrokeCap(androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp);
+ method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setStrokeCap(int);
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
}
@@ -715,6 +722,16 @@
method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setOverflow(int);
}
+ public static final class LayoutElementBuilders.StrokeCapProp {
+ method public int getValue();
+ }
+
+ public static final class LayoutElementBuilders.StrokeCapProp.Builder {
+ ctor public LayoutElementBuilders.StrokeCapProp.Builder();
+ method public androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp build();
+ method public androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp.Builder setValue(int);
+ }
+
public static final class LayoutElementBuilders.Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint? getLayoutConstraintsForDynamicText();
diff --git a/wear/protolayout/protolayout/api/public_plus_experimental_current.txt b/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
index 56d09d9..5d8554a 100644
--- a/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
+++ b/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
@@ -346,6 +346,10 @@
field public static final int SPAN_VERTICAL_ALIGN_BOTTOM = 1; // 0x1
field public static final int SPAN_VERTICAL_ALIGN_TEXT_BASELINE = 2; // 0x2
field public static final int SPAN_VERTICAL_ALIGN_UNDEFINED = 0; // 0x0
+ field public static final int STROKE_CAP_BUTT = 1; // 0x1
+ field public static final int STROKE_CAP_ROUND = 2; // 0x2
+ field public static final int STROKE_CAP_SQUARE = 3; // 0x3
+ field public static final int STROKE_CAP_UNDEFINED = 0; // 0x0
field public static final int TEXT_ALIGN_CENTER = 2; // 0x2
field public static final int TEXT_ALIGN_END = 3; // 0x3
field public static final int TEXT_ALIGN_START = 1; // 0x1
@@ -427,6 +431,7 @@
method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicLength();
method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+ method public androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp? getStrokeCap();
method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
}
@@ -437,6 +442,8 @@
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLayoutConstraintsForDynamicLength(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+ method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setStrokeCap(androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp);
+ method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setStrokeCap(int);
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
}
@@ -758,6 +765,16 @@
method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setOverflow(int);
}
+ public static final class LayoutElementBuilders.StrokeCapProp {
+ method public int getValue();
+ }
+
+ public static final class LayoutElementBuilders.StrokeCapProp.Builder {
+ ctor public LayoutElementBuilders.StrokeCapProp.Builder();
+ method public androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp build();
+ method public androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp.Builder setValue(int);
+ }
+
public static final class LayoutElementBuilders.Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
method @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle? getAndroidTextStyle();
method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index 69c5e47..208a7d4 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -333,6 +333,10 @@
field public static final int SPAN_VERTICAL_ALIGN_BOTTOM = 1; // 0x1
field public static final int SPAN_VERTICAL_ALIGN_TEXT_BASELINE = 2; // 0x2
field public static final int SPAN_VERTICAL_ALIGN_UNDEFINED = 0; // 0x0
+ field public static final int STROKE_CAP_BUTT = 1; // 0x1
+ field public static final int STROKE_CAP_ROUND = 2; // 0x2
+ field public static final int STROKE_CAP_SQUARE = 3; // 0x3
+ field public static final int STROKE_CAP_UNDEFINED = 0; // 0x0
field public static final int TEXT_ALIGN_CENTER = 2; // 0x2
field public static final int TEXT_ALIGN_END = 3; // 0x3
field public static final int TEXT_ALIGN_START = 1; // 0x1
@@ -403,6 +407,7 @@
method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicLength();
method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
+ method public androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp? getStrokeCap();
method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
}
@@ -413,6 +418,8 @@
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLayoutConstraintsForDynamicLength(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
+ method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setStrokeCap(androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp);
+ method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setStrokeCap(int);
method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
}
@@ -715,6 +722,16 @@
method public androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder setOverflow(int);
}
+ public static final class LayoutElementBuilders.StrokeCapProp {
+ method public int getValue();
+ }
+
+ public static final class LayoutElementBuilders.StrokeCapProp.Builder {
+ ctor public LayoutElementBuilders.StrokeCapProp.Builder();
+ method public androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp build();
+ method public androidx.wear.protolayout.LayoutElementBuilders.StrokeCapProp.Builder setValue(int);
+ }
+
public static final class LayoutElementBuilders.Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint? getLayoutConstraintsForDynamicText();
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 ddfea03..4b2cf21 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
@@ -219,6 +219,46 @@
*/
public static final int CONTENT_SCALE_MODE_FILL_BOUNDS = 3;
+ /**
+ * Styles to use for path endings.
+ *
+ * @since 1.2
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @IntDef({STROKE_CAP_UNDEFINED, STROKE_CAP_BUTT, STROKE_CAP_ROUND, STROKE_CAP_SQUARE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StrokeCap {}
+
+ /**
+ * {@code StrokeCap} is undefined.
+ *
+ * @since 1.2
+ */
+ public static final int STROKE_CAP_UNDEFINED = 0;
+
+ /**
+ * Begin and end contours with a flat edge and no extension.
+ *
+ * @since 1.2
+ */
+ public static final int STROKE_CAP_BUTT = 1;
+
+ /**
+ * Begin and end contours with a semi-circle extension. The extension size is proportional to
+ * the thickness of the path.
+ *
+ * @since 1.2
+ */
+ public static final int STROKE_CAP_ROUND = 2;
+
+ /**
+ * Begin and end contours with a half square extension. The extension size is proportional to
+ * the thickness of the path.
+ *
+ * @since 1.2
+ */
+ public static final int STROKE_CAP_SQUARE = 3;
+
/** An extensible {@code FontWeight} property. */
public static final class FontWeightProp {
private final LayoutElementProto.FontWeightProp mImpl;
@@ -3690,6 +3730,20 @@
}
}
+ /**
+ * Gets the line stroke cap. If not defined, defaults to STROKE_CAP_ROUND.
+ *
+ * @since 1.2
+ */
+ @Nullable
+ public StrokeCapProp getStrokeCap() {
+ if (mImpl.hasStrokeCap()) {
+ return StrokeCapProp.fromProto(mImpl.getStrokeCap());
+ } else {
+ return null;
+ }
+ }
+
@Override
@RestrictTo(Scope.LIBRARY_GROUP)
@Nullable
@@ -3718,7 +3772,7 @@
public static final class Builder implements ArcLayoutElement.Builder {
private final LayoutElementProto.ArcLine.Builder mImpl =
LayoutElementProto.ArcLine.newBuilder();
- private final Fingerprint mFingerprint = new Fingerprint(-1371793535);
+ private final Fingerprint mFingerprint = new Fingerprint(846148011);
public Builder() {}
@@ -3798,6 +3852,33 @@
return this;
}
+ /**
+ * Sets the line stroke cap. If not defined, defaults to STROKE_CAP_ROUND.
+ *
+ * @since 1.2
+ */
+ @NonNull
+ public Builder setStrokeCap(@NonNull StrokeCapProp strokeCap) {
+ mImpl.setStrokeCap(strokeCap.toProto());
+ mFingerprint.recordPropertyUpdate(
+ 6, checkNotNull(strokeCap.getFingerprint()).aggregateValueAsInt());
+ return this;
+ }
+
+ /**
+ * Sets the line stroke cap. If not defined, defaults to STROKE_CAP_ROUND.
+ *
+ * @since 1.2
+ */
+ @NonNull
+ public Builder setStrokeCap(@StrokeCap int strokeCap) {
+ mImpl.setStrokeCap(
+ LayoutElementProto.StrokeCapProp.newBuilder()
+ .setValue(LayoutElementProto.StrokeCap.forNumber(strokeCap)));
+ mFingerprint.recordPropertyUpdate(6, strokeCap);
+ return this;
+ }
+
@Override
@NonNull
public ArcLine build() {
@@ -3812,6 +3893,86 @@
}
}
+ /**
+ * An extensible {@code StrokeCap} property.
+ *
+ * @since 1.2
+ */
+ public static final class StrokeCapProp {
+ private final LayoutElementProto.StrokeCapProp mImpl;
+ @Nullable private final Fingerprint mFingerprint;
+
+ StrokeCapProp(LayoutElementProto.StrokeCapProp impl, @Nullable Fingerprint fingerprint) {
+ this.mImpl = impl;
+ this.mFingerprint = fingerprint;
+ }
+
+ /**
+ * Gets the value.
+ *
+ * @since 1.2
+ */
+ @StrokeCap
+ public int getValue() {
+ return mImpl.getValue().getNumber();
+ }
+
+ /** 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 StrokeCapProp fromProto(
+ @NonNull LayoutElementProto.StrokeCapProp proto,
+ @Nullable Fingerprint fingerprint) {
+ return new StrokeCapProp(proto, fingerprint);
+ }
+
+ @NonNull
+ static StrokeCapProp fromProto(@NonNull LayoutElementProto.StrokeCapProp proto) {
+ return fromProto(proto, null);
+ }
+
+ /** Returns the internal proto instance. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public LayoutElementProto.StrokeCapProp toProto() {
+ return mImpl;
+ }
+
+ /** Builder for {@link StrokeCapProp} */
+ public static final class Builder {
+ private final LayoutElementProto.StrokeCapProp.Builder mImpl =
+ LayoutElementProto.StrokeCapProp.newBuilder();
+ private final Fingerprint mFingerprint = new Fingerprint(-956183418);
+
+ public Builder() {}
+
+ /**
+ * Sets the value.
+ *
+ * @since 1.2
+ */
+ @NonNull
+ public Builder setValue(@StrokeCap int value) {
+ mImpl.setValue(LayoutElementProto.StrokeCap.forNumber(value));
+ mFingerprint.recordPropertyUpdate(1, value);
+ return this;
+ }
+
+ /** Builds an instance from accumulated values. */
+ @NonNull
+ public StrokeCapProp build() {
+ return new StrokeCapProp(mImpl.build(), mFingerprint);
+ }
+ }
+ }
+
/** A simple spacer used to provide padding between adjacent elements in an {@link Arc}. */
public static final class ArcSpacer implements ArcLayoutElement {
private final LayoutElementProto.ArcSpacer mImpl;
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 78945dd..3b6107e 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
@@ -22,6 +22,7 @@
import androidx.wear.protolayout.expression.DynamicBuilders;
import androidx.wear.protolayout.proto.DimensionProto;
+import androidx.wear.protolayout.proto.LayoutElementProto;
import androidx.wear.protolayout.proto.TypesProto;
import org.junit.Test;
@@ -79,6 +80,34 @@
}
@Test
+ public void testArcLineSetStrokeCap() {
+ LayoutElementBuilders.ArcLine arcLine =
+ new LayoutElementBuilders.ArcLine.Builder()
+ .setStrokeCap(LayoutElementBuilders.STROKE_CAP_BUTT)
+ .build();
+
+ LayoutElementProto.StrokeCapProp strokeCapProp = arcLine.toProto().getStrokeCap();
+
+ assertThat(strokeCapProp.getValue())
+ .isEqualTo(LayoutElementProto.StrokeCap.STROKE_CAP_BUTT);
+ }
+
+ @Test
+ public void testArcLineSetStrokeCapProp() {
+ LayoutElementBuilders.ArcLine arcLine =
+ new LayoutElementBuilders.ArcLine.Builder()
+ .setStrokeCap(new LayoutElementBuilders.StrokeCapProp.Builder()
+ .setValue(LayoutElementBuilders.STROKE_CAP_BUTT)
+ .build())
+ .build();
+
+ LayoutElementProto.StrokeCapProp strokeCapProp = arcLine.toProto().getStrokeCap();
+
+ assertThat(strokeCapProp.getValue())
+ .isEqualTo(LayoutElementProto.StrokeCap.STROKE_CAP_BUTT);
+ }
+
+ @Test
public void arcLineSetLength_withoutLayoutConstraint_throws() {
assertThrows(
IllegalStateException.class,