public class

Chip

extends java.lang.Object

implements LayoutElementBuilders.LayoutElement

 java.lang.Object

↳androidx.wear.protolayout.materialcore.Chip

Gradle dependencies

compile group: 'androidx.wear.protolayout', name: 'protolayout-material-core', version: '1.2.0'

  • groupId: androidx.wear.protolayout
  • artifactId: protolayout-material-core
  • version: 1.2.0

Artifact androidx.wear.protolayout:protolayout-material-core:1.2.0 it located at Google repository (https://maven.google.com/)

Overview

ProtoLayout core component Chip that represents clickable object with the text, optional label and optional icon or with custom content. This component is not meant to be used standalone, it's a helper component for the Material library.

The Chip is Stadium shape object. The recommended sizes and styles are defined in the public Material library.

This Button doesn't have any styling applied, that should be done by the calling library.

When accessing the contents of a container for testing, note that this element can't be simply casted back to the original type, i.e.:

 Chip chip = new Chip...
 Box box = new Box.Builder().addContent(chip).build();

 Chip myChip = (Chip) box.getContents().get(0);
 
will fail.

To be able to get Chip object from any layout element, Chip.fromLayoutElement(LayoutElementBuilders.LayoutElement) method should be used, i.e.:

 Chip myChip = Chip.fromLayoutElement(box.getContents().get(0));
 

Summary

Fields
public static final java.lang.StringMETADATA_TAG_CUSTOM_CONTENT

Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with custom content.

public static final java.lang.StringMETADATA_TAG_ICON

Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with icon.

public static final java.lang.StringMETADATA_TAG_TEXT

Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with only text.

Methods
public static ChipfromLayoutElement(LayoutElementBuilders.LayoutElement element)

Returns Chip object from the given LayoutElement (e.g.

public ColorBuilders.ColorPropgetBackgroundColor()

Returns background color of this Chip.

public ModifiersBuilders.ClickablegetClickable()

Returns click event action associated with this Chip.

public TypeBuilders.StringPropgetContentDescription()

Returns content description of this Chip.

public LayoutElementBuilders.LayoutElementgetCustomContent()

Returns custom content from this Chip if it has been added.

public FingerprintgetFingerprint()

public DimensionBuilders.ContainerDimensiongetHeight()

Returns the visible height of this Chip.

public intgetHorizontalAlignment()

Returns the horizontal alignment of the content in this Chip.

public LayoutElementBuilders.LayoutElementgetIconContent()

Returns icon id from this Chip if it has been added.

public java.lang.StringgetMetadataTag()

Returns metadata tag set to this Chip.

public LayoutElementBuilders.LayoutElementgetPrimaryLabelContent()

Returns primary label from this Chip if it has been added.

public LayoutElementBuilders.LayoutElementgetSecondaryLabelContent()

Returns secondary label from this Chip if it has been added.

public DimensionBuilders.ContainerDimensiongetWidth()

Returns width of this Chip.

public LayoutElementProto.LayoutElementtoLayoutElementProto()

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final java.lang.String METADATA_TAG_TEXT

Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with only text.

public static final java.lang.String METADATA_TAG_ICON

Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with icon.

public static final java.lang.String METADATA_TAG_CUSTOM_CONTENT

Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with custom content.

Methods

Returns the visible height of this Chip.

Returns width of this Chip.

public ModifiersBuilders.Clickable getClickable()

Returns click event action associated with this Chip.

public ColorBuilders.ColorProp getBackgroundColor()

Returns background color of this Chip.

public TypeBuilders.StringProp getContentDescription()

Returns content description of this Chip.

public LayoutElementBuilders.LayoutElement getCustomContent()

Returns custom content from this Chip if it has been added. Otherwise, it returns null.

public LayoutElementBuilders.LayoutElement getPrimaryLabelContent()

Returns primary label from this Chip if it has been added. Otherwise, it returns null.

public LayoutElementBuilders.LayoutElement getSecondaryLabelContent()

Returns secondary label from this Chip if it has been added. Otherwise, it returns null.

public LayoutElementBuilders.LayoutElement getIconContent()

Returns icon id from this Chip if it has been added. Otherwise, it returns null.

public int getHorizontalAlignment()

Returns the horizontal alignment of the content in this Chip.

public java.lang.String getMetadataTag()

Returns metadata tag set to this Chip.

public static Chip fromLayoutElement(LayoutElementBuilders.LayoutElement element)

Returns Chip object from the given LayoutElement (e.g. one retrieved from a container's content with container.getContents().get(index)) if that element can be converted to Chip. Otherwise, it will return null.

public LayoutElementProto.LayoutElement toLayoutElementProto()

public Fingerprint getFingerprint()

Source

/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.wear.protolayout.materialcore;

import static androidx.wear.protolayout.ColorBuilders.argb;
import static androidx.wear.protolayout.DimensionBuilders.dp;
import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_START;
import static androidx.wear.protolayout.materialcore.Helper.checkNotNull;
import static androidx.wear.protolayout.materialcore.Helper.checkTag;
import static androidx.wear.protolayout.materialcore.Helper.getMetadataTagName;
import static androidx.wear.protolayout.materialcore.Helper.getTagBytes;
import static androidx.wear.protolayout.materialcore.Helper.radiusOf;

import static java.lang.Math.max;

import android.annotation.SuppressLint;
import android.graphics.Color;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.wear.protolayout.ColorBuilders.ColorProp;
import androidx.wear.protolayout.DimensionBuilders.ContainerDimension;
import androidx.wear.protolayout.DimensionBuilders.DpProp;
import androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp;
import androidx.wear.protolayout.LayoutElementBuilders;
import androidx.wear.protolayout.LayoutElementBuilders.Box;
import androidx.wear.protolayout.LayoutElementBuilders.Column;
import androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignment;
import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
import androidx.wear.protolayout.LayoutElementBuilders.Row;
import androidx.wear.protolayout.LayoutElementBuilders.Spacer;
import androidx.wear.protolayout.ModifiersBuilders.Background;
import androidx.wear.protolayout.ModifiersBuilders.Clickable;
import androidx.wear.protolayout.ModifiersBuilders.Corner;
import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
import androidx.wear.protolayout.ModifiersBuilders.Padding;
import androidx.wear.protolayout.ModifiersBuilders.Semantics;
import androidx.wear.protolayout.TypeBuilders.StringProp;
import androidx.wear.protolayout.expression.Fingerprint;
import androidx.wear.protolayout.proto.LayoutElementProto;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;

/**
 * ProtoLayout core component {@link Chip} that represents clickable object with the text, optional
 * label and optional icon or with custom content. This component is not meant to be used
 * standalone, it's a helper component for the Material library.
 *
 * <p>The Chip is Stadium shape object. The recommended sizes and styles are defined in the public
 * Material library.
 *
 * <p>This Button doesn't have any styling applied, that should be done by the calling library.
 *
 * <p>When accessing the contents of a container for testing, note that this element can't be simply
 * casted back to the original type, i.e.:
 *
 * <pre>{@code
 * Chip chip = new Chip...
 * Box box = new Box.Builder().addContent(chip).build();
 *
 * Chip myChip = (Chip) box.getContents().get(0);
 * }</pre>
 *
 * will fail.
 *
 * <p>To be able to get {@link Chip} object from any layout element, {@link #fromLayoutElement}
 * method should be used, i.e.:
 *
 * <pre>{@code
 * Chip myChip = Chip.fromLayoutElement(box.getContents().get(0));
 * }</pre>
 */
public class Chip implements LayoutElement {
    /**
     * Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with only text.
     */
    public static final String METADATA_TAG_TEXT = "TXTCHP";

    /** Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with icon. */
    public static final String METADATA_TAG_ICON = "ICNCHP";

    /**
     * Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with custom
     * content.
     */
    public static final String METADATA_TAG_CUSTOM_CONTENT = "CSTCHP";

    private static final int PRIMARY_LABEL_INDEX = 0;
    private static final int SECONDARY_LABEL_INDEX = 1;
    private static final int LABELS_INDEX_NO_ICON = 0;
    private static final int LABELS_INDEX_ICON = 2;

    /** Outer tappable Box. */
    @NonNull private final Box mImpl;

    /** Inner visible Box with all Chip elements. */
    @NonNull private final Box mElement;

    Chip(@NonNull Box impl) {
        mImpl = impl;
        mElement = (Box) impl.getContents().get(0);
    }

    /** Builder class for {@link Chip}. */
    public static final class Builder implements LayoutElement.Builder {
        /** Chip type that has no inner set. */
        public static final int NOT_SET = 0;

        /** Chip type to be used when setting a content which has a text. */
        public static final int TEXT = 1;

        /** Chip type to be used when setting a content which has an icon. */
        public static final int ICON = 2;

        /** Chip type to be used when setting a content which is a custom one. */
        public static final int CUSTOM_CONTENT = 3;

        /** Chip types. */
        @RestrictTo(Scope.LIBRARY_GROUP)
        @Retention(RetentionPolicy.SOURCE)
        @IntDef({NOT_SET, TEXT, ICON, CUSTOM_CONTENT})
        public @interface ChipType {}

        @Nullable private LayoutElement mCustomContent;
        @Nullable private LayoutElement mIconContent = null;
        @Nullable private LayoutElement mPrimaryLabelContent = null;
        @Nullable private LayoutElement mSecondaryLabelContent = null;
        @NonNull private final Clickable mClickable;
        @Nullable private StringProp mContentDescription = null;
        @NonNull private ContainerDimension mWidth = dp(0);
        @NonNull private DpProp mHeight = dp(0);
        @NonNull private ColorProp mBackgroundColor = argb(Color.BLACK);
        @HorizontalAlignment private int mHorizontalAlign = HORIZONTAL_ALIGN_START;
        @NonNull private DpProp mHorizontalPadding = dp(0);
        @NonNull private DpProp mIconSpacerWidth = dp(0);
        @NonNull private DpProp mMinTappableSquareLength = dp(0);

        @NonNull static final Map<Integer, String> TYPE_TO_TAG = new HashMap<>();

        static {
            TYPE_TO_TAG.put(ICON, METADATA_TAG_ICON);
            TYPE_TO_TAG.put(TEXT, METADATA_TAG_TEXT);
            TYPE_TO_TAG.put(CUSTOM_CONTENT, METADATA_TAG_CUSTOM_CONTENT);
        }

        /**
         * Creates a builder for the {@link Chip} with associated action. It is required to add
         * content later with setters.
         *
         * @param clickable Associated {@link Clickable} for click events. When the Chip is clicked
         *     it will fire the associated action.
         */
        public Builder(@NonNull Clickable clickable) {
            mClickable = clickable;
        }

        /** Sets the width of {@link Chip}. If not set, Chip won't be shown. */
        @NonNull
        public Builder setWidth(@NonNull ContainerDimension width) {
            mWidth = width;
            return this;
        }

        /** Sets the height of {@link Chip}. If not set, Chip won't be shown. */
        @NonNull
        public Builder setHeight(@NonNull DpProp height) {
            mHeight = height;
            return this;
        }

        /**
         * Sets the custom content for the {@link Chip}. Any previously added content will be
         * overridden. Provided content should be styled and sized.
         */
        @NonNull
        public Builder setCustomContent(@NonNull LayoutElement content) {
            this.mCustomContent = content;
            this.mPrimaryLabelContent = null;
            this.mSecondaryLabelContent = null;
            this.mIconContent = null;
            return this;
        }

        /** Sets the background colors for the {@link Button}. If not set, black is used. */
        @NonNull
        public Builder setBackgroundColor(@NonNull ColorProp backgroundColor) {
            mBackgroundColor = backgroundColor;
            return this;
        }

        /**
         * Sets the content description for the {@link Chip}. It is highly recommended to provide
         * this for chip containing icon.
         *
         * <p>While this field is statically accessible from 1.0, it's only bindable since version
         * 1.2 and renderers supporting version 1.2 will use the dynamic value (if set).
         */
        @NonNull
        public Builder setContentDescription(@NonNull StringProp contentDescription) {
            this.mContentDescription = contentDescription;
            return this;
        }

        /**
         * Sets the primary label for the {@link Chip}. Any previously added custom content will be
         * overridden. This should be styled and sized by the caller.
         */
        @NonNull
        public Builder setPrimaryLabelContent(@NonNull LayoutElement primaryLabel) {
            this.mPrimaryLabelContent = primaryLabel;
            this.mCustomContent = null;
            return this;
        }

        /**
         * Sets the secondary label for the {@link Chip}. Any previously added custom content will
         * be overridden. If secondary label is set, primary label must be set too with {@link
         * #setPrimaryLabelContent}. This should be styled and sized by the caller.
         */
        @NonNull
        public Builder setSecondaryLabelContent(@NonNull LayoutElement secondaryLabel) {
            this.mSecondaryLabelContent = secondaryLabel;
            this.mCustomContent = null;
            return this;
        }

        /**
         * Sets the icon for the {@link Chip}. Any previously added custom content will be
         * overridden. If icon is set, primary label must be set too with {@link
         * #setPrimaryLabelContent}. This should be styled and sized by the caller.
         */
        @NonNull
        public Builder setIconContent(@NonNull LayoutElement imageResourceId) {
            this.mIconContent = imageResourceId;
            this.mCustomContent = null;
            return this;
        }

        /**
         * Sets the horizontal alignment in the chip. If not set, {@link
         * HorizontalAlignment#HORIZONTAL_ALIGN_START} will be used.
         */
        @NonNull
        public Builder setHorizontalAlignment(@HorizontalAlignment int horizontalAlignment) {
            mHorizontalAlign = horizontalAlignment;
            return this;
        }

        /** Sets the width of spacer used next to the icon if set. */
        @NonNull
        public Builder setIconSpacerWidth(@NonNull DpProp iconSpacerWidth) {
            mIconSpacerWidth = iconSpacerWidth;
            return this;
        }

        /** Sets the length of minimal tappable square for this chip. */
        @NonNull
        public Builder setMinimalTappableSquareLength(@NonNull DpProp tappableLength) {
            mMinTappableSquareLength = tappableLength;
            return this;
        }

        /** Sets the horizontal padding in the chip. */
        @NonNull
        public Builder setHorizontalPadding(@NonNull DpProp horizontalPadding) {
            this.mHorizontalPadding = horizontalPadding;
            return this;
        }

        /** Constructs and returns {@link Chip} with the provided content and look. */
        @NonNull
        @Override
        public Chip build() {
            Modifiers.Builder modifiers =
                    new Modifiers.Builder()
                            .setClickable(mClickable)
                            .setPadding(
                                    new Padding.Builder()
                                            .setStart(mHorizontalPadding)
                                            .setEnd(mHorizontalPadding)
                                            .build())
                            .setBackground(
                                    new Background.Builder()
                                            .setColor(mBackgroundColor)
                                            .setCorner(
                                                    new Corner.Builder()
                                                            .setRadius(radiusOf(mHeight))
                                                            .build())
                                            .build())
                            .setSemantics(
                                    new Semantics.Builder()
                                        .setContentDescription(getCorrectContentDescription())
                                        .build());

            Box.Builder visible =
                    new Box.Builder()
                            .setHeight(mHeight)
                            .setWidth(mWidth)
                            .setHorizontalAlignment(mHorizontalAlign)
                            .addContent(getCorrectContent())
                            .setModifiers(modifiers.build());

            // Following accessibility guide, the renderer will attempt to extend the clickable's
            // touch target size to a minimum of 48dp when inflating it. Since this touch extension
            // is not layout affecting, thus it is not guaranteed unless there is enough space
            // around it. This wrapper ensures that there is enough space for this extended touch
            // target.
            Box wrapperForTapTarget =
                    new Box.Builder()
                            .setWidth(resolveMinTappableWidth())
                            .setHeight(dp(resolveMinTappableHeight()))
                            .setModifiers(
                                new Modifiers.Builder()
                                    .setMetadata(getCorrectMetadataTag())
                                    .build())
                            .addContent(visible.build())
                            .build();

            return new Chip(wrapperForTapTarget);
        }

        private ContainerDimension resolveMinTappableWidth() {
            if (mWidth instanceof DpProp) {
                return dp(max(((DpProp) mWidth).getValue(), mMinTappableSquareLength.getValue()));
            } else if (mWidth instanceof WrappedDimensionProp) {
                WrappedDimensionProp widthWrap = ((WrappedDimensionProp) mWidth);
                return new WrappedDimensionProp.Builder()
                        .setMinimumSize(
                                dp(
                                        max(
                                                widthWrap.getMinimumSize() != null
                                                        ? widthWrap.getMinimumSize().getValue()
                                                        : 0,
                                                mMinTappableSquareLength.getValue())))
                        .build();
            } else {
                return mWidth;
            }
        }

        private float resolveMinTappableHeight() {
            return max(mHeight.getValue(), mMinTappableSquareLength.getValue());
        }

        @NonNull
        private StringProp getCorrectContentDescription() {
            if (mContentDescription == null) {
                String staticValue = "";
                if (mPrimaryLabelContent != null) {
                    staticValue += mPrimaryLabelContent;
                }
                if (mSecondaryLabelContent != null) {
                    staticValue += "\n" + mSecondaryLabelContent;
                }
                mContentDescription = new StringProp.Builder(staticValue).build();
            }
            return checkNotNull(mContentDescription);
        }

        private ElementMetadata getCorrectMetadataTag() {
            String tag = METADATA_TAG_TEXT;
            if (mCustomContent != null) {
                tag = METADATA_TAG_CUSTOM_CONTENT;
            } else if (mIconContent != null) {
                tag = METADATA_TAG_ICON;
            }
            return new ElementMetadata.Builder().setTagData(getTagBytes(tag)).build();
        }

        @SuppressLint("CheckResult") // (b/247804720)
        @NonNull
        private LayoutElement getCorrectContent() {
            if (mCustomContent != null) {
                return mCustomContent;
            }

            if (mPrimaryLabelContent == null
                    && mSecondaryLabelContent == null
                    && mIconContent != null) {
                // Icon only variant of chip.
                return mIconContent;
            }

            Column.Builder column =
                    new Column.Builder()
                            .setHorizontalAlignment(HORIZONTAL_ALIGN_START)
                            .addContent(putLayoutInBox(checkNotNull(mPrimaryLabelContent)).build());

            if (mSecondaryLabelContent != null) {
                column.addContent(putLayoutInBox(mSecondaryLabelContent).build());
            }

            Box labels = putLayoutInBox(column.build()).build();
            if (mIconContent == null) {
                return labels;
            } else {
                return new Row.Builder()
                        .addContent(mIconContent)
                        .addContent(
                                new Spacer.Builder()
                                        .setHeight(mHeight)
                                        .setWidth(mIconSpacerWidth)
                                        .build())
                        .addContent(labels)
                        .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
                        .build();
            }
        }

        private Box.Builder putLayoutInBox(@NonNull LayoutElement element) {
            // Wrapped and centered content are default.
            return new Box.Builder().addContent(element);
        }
    }

    /** Returns the visible height of this Chip. */
    @NonNull
    public ContainerDimension getHeight() {
        return checkNotNull(mElement.getHeight());
    }

    /** Returns width of this Chip. */
    @NonNull
    public ContainerDimension getWidth() {
        return checkNotNull(mElement.getWidth());
    }

    /** Returns click event action associated with this Chip. */
    @NonNull
    public Clickable getClickable() {
        return checkNotNull(checkNotNull(mElement.getModifiers()).getClickable());
    }

    /** Returns background color of this Chip. */
    @NonNull
    public ColorProp getBackgroundColor() {
        return checkNotNull(
                checkNotNull(checkNotNull(mElement.getModifiers()).getBackground()).getColor());
    }

    /** Returns content description of this Chip. */
    @Nullable
    public StringProp getContentDescription() {
        // Semantics are applied to the visible view.
        Semantics semantics = checkNotNull(mElement.getModifiers()).getSemantics();
        if (semantics == null) {
            return null;
        }
        return semantics.getContentDescription();
    }

    /** Returns custom content from this Chip if it has been added. Otherwise, it returns null. */
    @Nullable
    public LayoutElement getCustomContent() {
        if (getMetadataTag().equals(METADATA_TAG_CUSTOM_CONTENT)) {
            return checkNotNull(checkNotNull(mElement.getContents()).get(0));
        }
        return null;
    }

    /** Returns primary label from this Chip if it has been added. Otherwise, it returns null. */
    @Nullable
    public LayoutElement getPrimaryLabelContent() {
        return getPrimaryOrSecondaryLabelContent(PRIMARY_LABEL_INDEX);
    }

    /** Returns secondary label from this Chip if it has been added. Otherwise, it returns null. */
    @Nullable
    public LayoutElement getSecondaryLabelContent() {
        return getPrimaryOrSecondaryLabelContent(SECONDARY_LABEL_INDEX);
    }

    /** Returns icon id from this Chip if it has been added. Otherwise, it returns null. */
    @Nullable
    public LayoutElement getIconContent() {
        if (!getMetadataTag().equals(METADATA_TAG_ICON)) {
            return null;
        }
        // TODO(b/330165026): Refactor to use bit in the metadata tag like layouts do, instead of
        // relying on the null here. The primary label can be null in case of icon only CompactChip.
        LayoutElement topLevel = mElement.getContents().get(0);
        return topLevel instanceof Row
                ? ((Row) mElement.getContents().get(0)).getContents().get(0)
                : topLevel;
    }

    @Nullable
    private LayoutElement getPrimaryOrSecondaryLabelContent(int index) {
        String metadataTag = getMetadataTag();
        if (metadataTag.equals(METADATA_TAG_CUSTOM_CONTENT)) {
            return null;
        }

        // In any other case, text (either primary or primary + label) must be present.
        Column content;
        if (metadataTag.equals(METADATA_TAG_ICON)) {
            if (!(mElement.getContents().get(0) instanceof Row)) {
                // This is icon only Chip, no label.
                return null;
            }

            content =
                    (Column)
                            ((Box)
                                            ((Row) mElement.getContents().get(0))
                                                    .getContents()
                                                    .get(LABELS_INDEX_ICON))
                                    .getContents()
                                    .get(0);
        } else {
            content =
                    (Column)
                            ((Box) mElement.getContents().get(0))
                                    .getContents()
                                    .get(LABELS_INDEX_NO_ICON);
        }

        // We need to check this as this can be the case when we called for label, which doesn't
        // exist.
        return index < content.getContents().size()
                ? ((Box) content.getContents().get(index)).getContents().get(0)
                : null;
    }

    /** Returns the horizontal alignment of the content in this Chip. */
    @HorizontalAlignment
    public int getHorizontalAlignment() {
        return checkNotNull(mElement.getHorizontalAlignment()).getValue();
    }

    /** Returns metadata tag set to this Chip. */
    @NonNull
    public String getMetadataTag() {
        return getMetadataTagName(checkNotNull(checkNotNull(mImpl.getModifiers()).getMetadata()));
    }

    /**
     * Returns Chip object from the given LayoutElement (e.g. one retrieved from a container's
     * content with {@code container.getContents().get(index)}) if that element can be converted to
     * Chip. Otherwise, it will return null.
     */
    @Nullable
    public static Chip fromLayoutElement(@NonNull LayoutElement element) {
        if (element instanceof Chip) {
            return (Chip) element;
        }
        if (!(element instanceof Box)) {
            return null;
        }
        Box boxElement = (Box) element;
        if (!checkTag(boxElement.getModifiers(), Builder.TYPE_TO_TAG.values())) {
            return null;
        }
        // Now we are sure that this element is a Chip.
        return new Chip(boxElement);
    }

    @NonNull
    @Override
    @RestrictTo(Scope.LIBRARY_GROUP)
    public LayoutElementProto.LayoutElement toLayoutElementProto() {
        return mImpl.toLayoutElementProto();
    }

    @Nullable
    @Override
    public Fingerprint getFingerprint() {
        return mImpl.getFingerprint();
    }
}