public class

PrimaryLayout

extends java.lang.Object

implements LayoutElementBuilders.LayoutElement

 java.lang.Object

↳androidx.wear.tiles.material.layouts.PrimaryLayout

Gradle dependencies

compile group: 'androidx.wear.tiles', name: 'tiles-material', version: '1.4.0'

  • groupId: androidx.wear.tiles
  • artifactId: tiles-material
  • version: 1.4.0

Artifact androidx.wear.tiles:tiles-material:1.4.0 it located at Google repository (https://maven.google.com/)

Overview

Tiles layout that represents a suggested layout style for Material Tiles with the primary (compact) chip at the bottom with the given content in the center and the recommended margin and padding applied. There is a fixed slot for an optional primary label above or optional secondary label below the main content area.

It is highly recommended that main content has max lines between 2 and 4 (dependant on labels present), i.e.: * No labels are present: content with max 4 lines, * 1 label is present: content with max 3 lines, * 2 labels are present: content with max 2 lines.

For additional examples and suggested layouts see Tiles Design System.

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.:

 PrimaryLayout pl = new PrimaryLayout...
 Box box = new Box.Builder().addContent(pl).build();

 PrimaryLayout myPl = (PrimaryLayout) box.getContents().get(0);
 
will fail.

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

 PrimaryLayout myPl = PrimaryLayout.fromLayoutElement(box.getContents().get(0));
 

Summary

Methods
public static PrimaryLayoutfromLayoutElement(LayoutElementBuilders.LayoutElement element)

Returns PrimaryLayout object from the given androidx.wear.tiles.LayoutElementBuilders.LayoutElement (e.g.

public LayoutElementBuilders.LayoutElementgetContent()

Get the inner content from this layout.

public FingerprintgetFingerprint()

public LayoutElementBuilders.LayoutElementgetPrimaryChipContent()

Get the primary chip content from this layout.

public LayoutElementBuilders.LayoutElementgetPrimaryLabelTextContent()

Get the primary label content from this layout.

public LayoutElementBuilders.LayoutElementgetSecondaryLabelTextContent()

Get the secondary label content from this layout.

public floatgetVerticalSpacerHeight()

Get the vertical spacer height from this layout.

public LayoutElementProto.LayoutElementtoLayoutElementProto()

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

Methods

public LayoutElementBuilders.LayoutElement getPrimaryLabelTextContent()

Get the primary label content from this layout.

public LayoutElementBuilders.LayoutElement getSecondaryLabelTextContent()

Get the secondary label content from this layout.

Get the inner content from this layout.

public LayoutElementBuilders.LayoutElement getPrimaryChipContent()

Get the primary chip content from this layout.

public float getVerticalSpacerHeight()

Get the vertical spacer height from this layout.

public static PrimaryLayout fromLayoutElement(LayoutElementBuilders.LayoutElement element)

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

public LayoutElementProto.LayoutElement toLayoutElementProto()

public Fingerprint getFingerprint()

Source

/*
 * Copyright 2022 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.tiles.material.layouts;

import static androidx.annotation.Dimension.DP;

import android.annotation.SuppressLint;

import androidx.annotation.Dimension;
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.expression.Fingerprint;
import androidx.wear.protolayout.proto.LayoutElementProto;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;

/**
 * Tiles layout that represents a suggested layout style for Material Tiles with the primary
 * (compact) chip at the bottom with the given content in the center and the recommended margin and
 * padding applied. There is a fixed slot for an optional primary label above or optional secondary
 * label below the main content area.
 *
 * <p>It is highly recommended that main content has max lines between 2 and 4 (dependant on labels
 * present), i.e.: * No labels are present: content with max 4 lines, * 1 label is present: content
 * with max 3 lines, * 2 labels are present: content with max 2 lines.
 *
 * <p>For additional examples and suggested layouts see <a
 * href="/training/wearables/design/tiles-design-system">Tiles Design System</a>.
 *
 * <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
 * PrimaryLayout pl = new PrimaryLayout...
 * Box box = new Box.Builder().addContent(pl).build();
 *
 * PrimaryLayout myPl = (PrimaryLayout) box.getContents().get(0);
 * }</pre>
 *
 * will fail.
 *
 * <p>To be able to get {@link PrimaryLayout} object from any layout element, {@link
 * #fromLayoutElement} method should be used, i.e.:
 *
 * <pre>{@code
 * PrimaryLayout myPl = PrimaryLayout.fromLayoutElement(box.getContents().get(0));
 * }</pre>
 *
 * @deprecated Use the new class {@link androidx.wear.protolayout.material.layouts.PrimaryLayout}
 *     which provides the same API and functionality.
 */
@Deprecated
@SuppressWarnings("deprecation")
public class PrimaryLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
    /**
     * Prefix tool tag for Metadata in androidx.wear.tiles.ModifiersBuilders.Modifiers, so we know
     * that androidx.wear.tiles.LayoutElementBuilders.Box is actually a PrimaryLayout.
     */
    static final String METADATA_TAG_PREFIX = "PL_";

    /** Index for byte array that contains bits to check whether the contents are present or not. */
    static final int FLAG_INDEX = METADATA_TAG_PREFIX.length();

    /**
     * Base tool tag for Metadata in androidx.wear.tiles.ModifiersBuilders.Modifiers, so we know
     * that androidx.wear.tiles.LayoutElementBuilders.Box is actually a PrimaryLayout and what
     * optional content is added.
     */
    static final byte[] METADATA_TAG_BASE =
            Arrays.copyOf(
                    androidx.wear.tiles.material.Helper.getTagBytes(METADATA_TAG_PREFIX),
                    FLAG_INDEX + 1);

    /**
     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
     * the primary chip is present or not.
     */
    static final int CHIP_PRESENT = 0x1;

    /**
     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
     * the primary label is present or not.
     */
    static final int PRIMARY_LABEL_PRESENT = 0x2;

    /**
     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
     * the secondary label is present or not.
     */
    static final int SECONDARY_LABEL_PRESENT = 0x4;

    /**
     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
     * the content is present or not.
     */
    static final int CONTENT_PRESENT = 0x8;

    /** Position of the primary label in its own inner column if exists. */
    static final int PRIMARY_LABEL_POSITION = 1;

    /** Position of the content in its own inner column. */
    static final int CONTENT_ONLY_POSITION = 0;

    /** Position of the primary chip in main layout column. */
    static final int PRIMARY_CHIP_POSITION = 1;

    @RestrictTo(RestrictTo.Scope.LIBRARY)
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(
            flag = true,
            value = {CHIP_PRESENT, PRIMARY_LABEL_PRESENT, SECONDARY_LABEL_PRESENT, CONTENT_PRESENT})
    @interface ContentBits {}

    @NonNull private final androidx.wear.tiles.LayoutElementBuilders.Box mImpl;

    // This contains inner columns and primary chip.
    @NonNull
    private final List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement> mAllContent;

    // This contains optional labels, spacers and main content.
    @NonNull
    private final List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement> mPrimaryLabel;

    // This contains optional labels, spacers and main content.
    @NonNull
    private final List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement>
            mContentAndSecondaryLabel;

    PrimaryLayout(@NonNull androidx.wear.tiles.LayoutElementBuilders.Box layoutElement) {
        this.mImpl = layoutElement;
        this.mAllContent =
                ((androidx.wear.tiles.LayoutElementBuilders.Column)
                                layoutElement.getContents().get(0))
                        .getContents();
        List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement> innerContent =
                ((androidx.wear.tiles.LayoutElementBuilders.Column) mAllContent.get(0))
                        .getContents();
        this.mPrimaryLabel =
                ((androidx.wear.tiles.LayoutElementBuilders.Column) innerContent.get(0))
                        .getContents();
        this.mContentAndSecondaryLabel =
                ((androidx.wear.tiles.LayoutElementBuilders.Column)
                                ((androidx.wear.tiles.LayoutElementBuilders.Box)
                                                innerContent.get(1))
                                        .getContents()
                                        .get(0))
                        .getContents();
    }

    /** Builder class for {@link PrimaryLayout}. */
    public static final class Builder
            implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
        @NonNull
        private final androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters
                mDeviceParameters;

        @Nullable
        private androidx.wear.tiles.LayoutElementBuilders.LayoutElement mPrimaryChip = null;

        @Nullable
        private androidx.wear.tiles.LayoutElementBuilders.LayoutElement mPrimaryLabelText = null;

        @Nullable
        private androidx.wear.tiles.LayoutElementBuilders.LayoutElement mSecondaryLabelText = null;

        @NonNull
        private androidx.wear.tiles.LayoutElementBuilders.LayoutElement mContent =
                new androidx.wear.tiles.LayoutElementBuilders.Box.Builder().build();

        @NonNull
        private androidx.wear.tiles.DimensionBuilders.DpProp mVerticalSpacerHeight =
                LayoutDefaults.DEFAULT_VERTICAL_SPACER_HEIGHT;

        private byte mMetadataContentByte = 0;

        /**
         * Creates a builder for the {@link PrimaryLayout} from the given content. Content inside of
         * it can later be set with {@link #setContent}, {@link #setPrimaryChipContent}, {@link
         * #setPrimaryLabelTextContent} and {@link #setSecondaryLabelTextContent}.
         */
        public Builder(
                @NonNull
                        androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters
                                deviceParameters) {
            this.mDeviceParameters = deviceParameters;
        }

        /**
         * Sets the element which is in the slot at the bottom of the layout. Note that it is
         * accepted to pass in any {@link androidx.wear.tiles.LayoutElementBuilders.LayoutElement},
         * but it is strongly recommended to add a {@link CompactChip} as the layout is optimized
         * for it.
         */
        @NonNull
        public Builder setPrimaryChipContent(
                @NonNull androidx.wear.tiles.LayoutElementBuilders.LayoutElement compactChip) {
            this.mPrimaryChip = compactChip;
            mMetadataContentByte = (byte) (mMetadataContentByte | CHIP_PRESENT);
            return this;
        }

        /** Sets the content in the primary label slot which will be above the main content. */
        @NonNull
        public Builder setPrimaryLabelTextContent(
                @NonNull androidx.wear.tiles.LayoutElementBuilders.LayoutElement primaryLabelText) {
            this.mPrimaryLabelText = primaryLabelText;
            mMetadataContentByte = (byte) (mMetadataContentByte | PRIMARY_LABEL_PRESENT);
            return this;
        }

        /**
         * Sets the content in the primary label slot which will be below the main content. It is
         * highly recommended to have primary label set when having secondary label.
         */
        @NonNull
        public Builder setSecondaryLabelTextContent(
                @NonNull
                        androidx.wear.tiles.LayoutElementBuilders.LayoutElement
                                secondaryLabelText) {
            this.mSecondaryLabelText = secondaryLabelText;
            mMetadataContentByte = (byte) (mMetadataContentByte | SECONDARY_LABEL_PRESENT);
            return this;
        }

        /**
         * Sets the additional content to this layout, above the primary chip.
         *
         * <p>The content slot will wrap the elements' height, so the height of the given content
         * must be fixed or set to wrap ({@code expand} can't be used).
         *
         * <p>This layout has built-in horizontal margins, so the given content should have width
         * set to {@code expand} to use all the available space, rather than an explicit width which
         * may lead to clipping.
         */
        @NonNull
        public Builder setContent(
                @NonNull androidx.wear.tiles.LayoutElementBuilders.LayoutElement content) {
            this.mContent = content;
            mMetadataContentByte = (byte) (mMetadataContentByte | CONTENT_PRESENT);
            return this;
        }

        /**
         * Sets the vertical spacer height which is used as a space between main content and
         * secondary label if there is any. If not set, {@link
         * LayoutDefaults#DEFAULT_VERTICAL_SPACER_HEIGHT} will be used.
         */
        @NonNull
        // The @Dimension(unit = DP) on dp() is seemingly being ignored, so lint complains that
        // we're passing PX to something expecting DP. Just suppress the warning for now.
        @SuppressLint("ResourceType")
        public Builder setVerticalSpacerHeight(@Dimension(unit = DP) float height) {
            this.mVerticalSpacerHeight = androidx.wear.tiles.DimensionBuilders.dp(height);
            return this;
        }

        /** Constructs and returns {@link PrimaryLayout} with the provided content and look. */
        // The @Dimension(unit = DP) on dp() is seemingly being ignored, so lint complains that
        // we're passing DP to something expecting PX. Just suppress the warning for now.
        @SuppressLint("ResourceType")
        @NonNull
        @Override
        public PrimaryLayout build() {
            float topPadding = getTopPadding();
            float bottomPadding = getBottomPadding();
            float horizontalPadding = getHorizontalPadding();
            float horizontalChipPadding = getChipHorizontalPadding();

            float primaryChipHeight =
                    mPrimaryChip != null
                            ? androidx.wear.tiles.material.ChipDefaults.COMPACT_HEIGHT_TAPPABLE
                                    .getValue()
                            : 0;

            androidx.wear.tiles.DimensionBuilders.DpProp mainContentHeight =
                    androidx.wear.tiles.DimensionBuilders.dp(
                            mDeviceParameters.getScreenHeightDp()
                                    - primaryChipHeight
                                    - bottomPadding
                                    - topPadding);

            // Layout organization: column(column(primary label + spacer + (box(column(content +
            // secondary label))) + chip)

            // First column that has all other content and chip.
            androidx.wear.tiles.LayoutElementBuilders.Column.Builder layoutBuilder =
                    new androidx.wear.tiles.LayoutElementBuilders.Column.Builder();

            // Contains primary label, main content and secondary label. Primary label will be
            // wrapped, while other content will be expanded so it can be centered in the remaining
            // space.
            androidx.wear.tiles.LayoutElementBuilders.Column.Builder contentAreaBuilder =
                    new androidx.wear.tiles.LayoutElementBuilders.Column.Builder()
                            .setWidth(androidx.wear.tiles.DimensionBuilders.expand())
                            .setHeight(mainContentHeight)
                            .setHorizontalAlignment(
                                    androidx.wear.tiles.LayoutElementBuilders
                                            .HORIZONTAL_ALIGN_CENTER);

            // Contains main content and secondary label with wrapped height so it can be put inside
            // of the androidx.wear.tiles.LayoutElementBuilders.Box to be centered.
            androidx.wear.tiles.LayoutElementBuilders.Column.Builder contentSecondaryLabelBuilder =
                    new androidx.wear.tiles.LayoutElementBuilders.Column.Builder()
                            .setWidth(androidx.wear.tiles.DimensionBuilders.expand())
                            .setHeight(androidx.wear.tiles.DimensionBuilders.wrap())
                            .setHorizontalAlignment(
                                    androidx.wear.tiles.LayoutElementBuilders
                                            .HORIZONTAL_ALIGN_CENTER);

            // Needs to be in column because of the spacers.
            androidx.wear.tiles.LayoutElementBuilders.Column.Builder primaryLabelBuilder =
                    new androidx.wear.tiles.LayoutElementBuilders.Column.Builder()
                            .setWidth(androidx.wear.tiles.DimensionBuilders.expand())
                            .setHeight(androidx.wear.tiles.DimensionBuilders.wrap());

            if (mPrimaryLabelText != null) {
                primaryLabelBuilder.addContent(
                        new androidx.wear.tiles.LayoutElementBuilders.Spacer.Builder()
                                .setHeight(getPrimaryLabelTopSpacerHeight())
                                .build());
                primaryLabelBuilder.addContent(mPrimaryLabelText);
            }

            contentAreaBuilder.addContent(primaryLabelBuilder.build());

            contentSecondaryLabelBuilder.addContent(
                    new androidx.wear.tiles.LayoutElementBuilders.Box.Builder()
                            .setVerticalAlignment(
                                    androidx.wear.tiles.LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
                            .setWidth(androidx.wear.tiles.DimensionBuilders.expand())
                            .setHeight(androidx.wear.tiles.DimensionBuilders.wrap())
                            .addContent(mContent)
                            .build());

            if (mSecondaryLabelText != null) {
                contentSecondaryLabelBuilder.addContent(
                        new androidx.wear.tiles.LayoutElementBuilders.Spacer.Builder()
                                .setHeight(mVerticalSpacerHeight)
                                .build());
                contentSecondaryLabelBuilder.addContent(mSecondaryLabelText);
            }

            contentAreaBuilder.addContent(
                    new androidx.wear.tiles.LayoutElementBuilders.Box.Builder()
                            .setVerticalAlignment(
                                    androidx.wear.tiles.LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
                            .setWidth(androidx.wear.tiles.DimensionBuilders.expand())
                            .setHeight(androidx.wear.tiles.DimensionBuilders.expand())
                            .addContent(contentSecondaryLabelBuilder.build())
                            .build());

            layoutBuilder
                    .setModifiers(
                            new androidx.wear.tiles.ModifiersBuilders.Modifiers.Builder()
                                    .setPadding(
                                            new androidx.wear.tiles.ModifiersBuilders.Padding
                                                            .Builder()
                                                    .setStart(
                                                            androidx.wear.tiles.DimensionBuilders
                                                                    .dp(horizontalPadding))
                                                    .setEnd(
                                                            androidx.wear.tiles.DimensionBuilders
                                                                    .dp(horizontalPadding))
                                                    .setTop(
                                                            androidx.wear.tiles.DimensionBuilders
                                                                    .dp(topPadding))
                                                    .setBottom(
                                                            androidx.wear.tiles.DimensionBuilders
                                                                    .dp(bottomPadding))
                                                    .build())
                                    .build())
                    .setWidth(androidx.wear.tiles.DimensionBuilders.expand())
                    .setHeight(androidx.wear.tiles.DimensionBuilders.expand())
                    .setHorizontalAlignment(
                            androidx.wear.tiles.LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER);

            layoutBuilder.addContent(contentAreaBuilder.build());

            if (mPrimaryChip != null) {
                layoutBuilder.addContent(
                        new androidx.wear.tiles.LayoutElementBuilders.Box.Builder()
                                .setVerticalAlignment(
                                        androidx.wear.tiles.LayoutElementBuilders
                                                .VERTICAL_ALIGN_BOTTOM)
                                .setWidth(androidx.wear.tiles.DimensionBuilders.expand())
                                .setHeight(androidx.wear.tiles.DimensionBuilders.wrap())
                                .setModifiers(
                                        new androidx.wear.tiles.ModifiersBuilders.Modifiers
                                                        .Builder()
                                                .setPadding(
                                                        new androidx.wear.tiles.ModifiersBuilders
                                                                        .Padding.Builder()
                                                                .setStart(
                                                                        androidx.wear.tiles
                                                                                .DimensionBuilders
                                                                                .dp(
                                                                                        horizontalChipPadding))
                                                                .setEnd(
                                                                        androidx.wear.tiles
                                                                                .DimensionBuilders
                                                                                .dp(
                                                                                        horizontalChipPadding))
                                                                .build())
                                                .build())
                                .addContent(mPrimaryChip)
                                .build());
            }

            byte[] metadata = METADATA_TAG_BASE.clone();
            metadata[FLAG_INDEX] = mMetadataContentByte;

            androidx.wear.tiles.LayoutElementBuilders.Box.Builder element =
                    new androidx.wear.tiles.LayoutElementBuilders.Box.Builder()
                            .setWidth(androidx.wear.tiles.DimensionBuilders.expand())
                            .setHeight(androidx.wear.tiles.DimensionBuilders.expand())
                            .setModifiers(
                                    new androidx.wear.tiles.ModifiersBuilders.Modifiers.Builder()
                                            .setMetadata(
                                                    new androidx.wear.tiles.ModifiersBuilders
                                                                    .ElementMetadata.Builder()
                                                            .setTagData(metadata)
                                                            .build())
                                            .build())
                            .setVerticalAlignment(
                                    androidx.wear.tiles.LayoutElementBuilders.VERTICAL_ALIGN_BOTTOM)
                            .addContent(layoutBuilder.build());

            return new PrimaryLayout(element.build());
        }

        /**
         * Returns the recommended bottom padding, based on percentage values in {@link
         * LayoutDefaults}.
         */
        private float getBottomPadding() {
            return mPrimaryChip != null
                    ? (mDeviceParameters.getScreenHeightDp()
                            * (androidx.wear.tiles.material.Helper.isRoundDevice(mDeviceParameters)
                                    ? LayoutDefaults.PRIMARY_LAYOUT_MARGIN_BOTTOM_ROUND_PERCENT
                                    : LayoutDefaults.PRIMARY_LAYOUT_MARGIN_BOTTOM_SQUARE_PERCENT))
                    : getTopPadding();
        }

        /**
         * Returns the recommended top padding, based on percentage values in {@link
         * LayoutDefaults}.
         */
        @Dimension(unit = DP)
        private float getTopPadding() {
            return mDeviceParameters.getScreenHeightDp()
                    * (androidx.wear.tiles.material.Helper.isRoundDevice(mDeviceParameters)
                            ? LayoutDefaults.PRIMARY_LAYOUT_MARGIN_TOP_ROUND_PERCENT
                            : LayoutDefaults.PRIMARY_LAYOUT_MARGIN_TOP_SQUARE_PERCENT);
        }

        /**
         * Returns the recommended horizontal padding, based on percentage values in {@link
         * LayoutDefaults}.
         */
        @Dimension(unit = DP)
        private float getHorizontalPadding() {
            return mDeviceParameters.getScreenWidthDp()
                    * (androidx.wear.tiles.material.Helper.isRoundDevice(mDeviceParameters)
                            ? LayoutDefaults.PRIMARY_LAYOUT_MARGIN_HORIZONTAL_ROUND_PERCENT
                            : LayoutDefaults.PRIMARY_LAYOUT_MARGIN_HORIZONTAL_SQUARE_PERCENT);
        }

        /**
         * Returns the recommended horizontal padding for primary chip, based on percentage values
         * and DP values in {@link LayoutDefaults}.
         */
        @Dimension(unit = DP)
        private float getChipHorizontalPadding() {
            return androidx.wear.tiles.material.Helper.isRoundDevice(mDeviceParameters)
                    ? LayoutDefaults.PRIMARY_LAYOUT_CHIP_HORIZONTAL_PADDING_ROUND_DP
                    : LayoutDefaults.PRIMARY_LAYOUT_CHIP_HORIZONTAL_PADDING_SQUARE_DP;
        }

        /** Returns the spacer height to be placed above primary label to accommodate Tile icon. */
        @NonNull
        private androidx.wear.tiles.DimensionBuilders.DpProp getPrimaryLabelTopSpacerHeight() {
            return androidx.wear.tiles.material.Helper.isRoundDevice(mDeviceParameters)
                    ? LayoutDefaults.PRIMARY_LAYOUT_PRIMARY_LABEL_SPACER_HEIGHT_ROUND_DP
                    : LayoutDefaults.PRIMARY_LAYOUT_PRIMARY_LABEL_SPACER_HEIGHT_SQUARE_DP;
        }
    }

    /** Get the primary label content from this layout. */
    @Nullable
    public androidx.wear.tiles.LayoutElementBuilders.LayoutElement getPrimaryLabelTextContent() {
        if (!areElementsPresent(PRIMARY_LABEL_PRESENT)) {
            return null;
        }
        return mPrimaryLabel.get(PRIMARY_LABEL_POSITION);
    }

    /** Get the secondary label content from this layout. */
    @Nullable
    public androidx.wear.tiles.LayoutElementBuilders.LayoutElement getSecondaryLabelTextContent() {
        if (!areElementsPresent(SECONDARY_LABEL_PRESENT)) {
            return null;
        }
        // By tag we know that secondary label exists. It will always be at last position.
        return mContentAndSecondaryLabel.get(mContentAndSecondaryLabel.size() - 1);
    }

    /** Get the inner content from this layout. */
    @Nullable
    public androidx.wear.tiles.LayoutElementBuilders.LayoutElement getContent() {
        if (!areElementsPresent(CONTENT_PRESENT)) {
            return null;
        }
        return ((androidx.wear.tiles.LayoutElementBuilders.Box)
                        mContentAndSecondaryLabel.get(CONTENT_ONLY_POSITION))
                .getContents()
                .get(0);
    }

    /** Get the primary chip content from this layout. */
    @Nullable
    public androidx.wear.tiles.LayoutElementBuilders.LayoutElement getPrimaryChipContent() {
        if (areElementsPresent(CHIP_PRESENT)) {
            return ((androidx.wear.tiles.LayoutElementBuilders.Box)
                            mAllContent.get(PRIMARY_CHIP_POSITION))
                    .getContents()
                    .get(0);
        }
        return null;
    }

    /** Get the vertical spacer height from this layout. */
    // The @Dimension(unit = DP) on getValue() is seemingly being ignored, so lint complains that
    // we're passing PX to something expecting DP. Just suppress the warning for now.
    @SuppressLint("ResourceType")
    @Dimension(unit = DP)
    public float getVerticalSpacerHeight() {
        if (areElementsPresent(SECONDARY_LABEL_PRESENT)) {
            androidx.wear.tiles.LayoutElementBuilders.LayoutElement element =
                    mContentAndSecondaryLabel.get(CONTENT_ONLY_POSITION + 1);
            if (element instanceof androidx.wear.tiles.LayoutElementBuilders.Spacer) {
                androidx.wear.tiles.DimensionBuilders.SpacerDimension height =
                        ((androidx.wear.tiles.LayoutElementBuilders.Spacer) element).getHeight();
                if (height instanceof androidx.wear.tiles.DimensionBuilders.DpProp) {
                    return ((androidx.wear.tiles.DimensionBuilders.DpProp) height).getValue();
                }
            }
        }
        return LayoutDefaults.DEFAULT_VERTICAL_SPACER_HEIGHT.getValue();
    }

    private boolean areElementsPresent(@ContentBits int elementFlag) {
        return (getMetadataTag()[FLAG_INDEX] & elementFlag) == elementFlag;
    }

    /** Returns metadata tag set to this PrimaryLayout. */
    @NonNull
    byte[] getMetadataTag() {
        return androidx.wear.tiles.material.Helper.getMetadataTagBytes(
                androidx.wear.tiles.material.Helper.checkNotNull(
                        androidx.wear.tiles.material.Helper.checkNotNull(mImpl.getModifiers())
                                .getMetadata()));
    }

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

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

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