public final class

Row

extends java.lang.Object

implements Item

 java.lang.Object

↳androidx.car.app.model.Row

Gradle dependencies

compile group: 'androidx.car.app', name: 'app', version: '1.2.0-rc01'

  • groupId: androidx.car.app
  • artifactId: app
  • version: 1.2.0-rc01

Artifact androidx.car.app:app:1.2.0-rc01 it located at Google repository (https://maven.google.com/)

Overview

Represents a row with a title, several lines of text, an optional image, and an optional action or switch.

Summary

Fields
public static final intIMAGE_TYPE_ICON

Represents a small image to be displayed in the row.

public static final intIMAGE_TYPE_LARGE

Represents a large image to be displayed in the row.

public static final intIMAGE_TYPE_SMALL

Represents a small image to be displayed in the row.

Methods
public booleanequals(java.lang.Object other)

public CarIcongetImage()

Returns the image to display in the row or null if the row does not contain an image.

public MetadatagetMetadata()

Returns the Metadata associated with the row or null if there is no metadata associated with the row.

public OnClickDelegategetOnClickDelegate()

Returns the OnClickListener to be called back when the row is clicked or null if the row is non-clickable.

public intgetRowImageType()

Returns the type of the image in the row.

public java.util.List<CarText>getTexts()

Returns the list of text below the title.

public CarTextgetTitle()

Returns the title of the row or null if not set.

public TogglegetToggle()

Returns the Toggle in the row or null if the row does not contain a toggle.

public inthashCode()

public booleanisBrowsable()

Returns whether the row is browsable.

public Rowrow()

Returns a Row for rowing Row.yourBoat()

public java.lang.StringtoString()

public java.lang.CharSequenceyourBoat()

Rows your boat.

from java.lang.Objectclone, finalize, getClass, notify, notifyAll, wait, wait, wait

Fields

public static final int IMAGE_TYPE_SMALL

Represents a small image to be displayed in the row.

To minimize scaling artifacts across a wide range of car screens, apps should provide images targeting a 88 x 88 dp bounding box. If necessary, the image will be scaled down while preserving its aspect ratio.

public static final int IMAGE_TYPE_LARGE

Represents a large image to be displayed in the row.

To minimize scaling artifacts across a wide range of car screens, apps should provide images targeting a 224 x 224 dp bounding box. If necessary, the image will be scaled down while preserving its aspect ratio.

public static final int IMAGE_TYPE_ICON

Represents a small image to be displayed in the row.

To minimize scaling artifacts across a wide range of car screens, apps should provide images targeting a 88 x 88 dp bounding box. If necessary, the icon will be scaled down while preserving its aspect ratio.

A tint color is expected to be provided via CarIcon.Builder.setTint(CarColor). Otherwise, a default tint color as determined by the host will be applied.

Methods

public CarText getTitle()

Returns the title of the row or null if not set.

See also: Row.Builder.setTitle(CharSequence)

public java.util.List<CarText> getTexts()

Returns the list of text below the title.

See also: Row.Builder.addText(CharSequence)

public CarIcon getImage()

Returns the image to display in the row or null if the row does not contain an image.

See also: Row.Builder.setImage(CarIcon), Row.Builder.setImage(CarIcon, int)

public int getRowImageType()

Returns the type of the image in the row.

public Toggle getToggle()

Returns the Toggle in the row or null if the row does not contain a toggle.

See also: Row.Builder.setToggle(Toggle)

public boolean isBrowsable()

Returns whether the row is browsable.

If a row is browsable, then no Action or Toggle can be added to it.

See also: Row.isBrowsable()

public OnClickDelegate getOnClickDelegate()

Returns the OnClickListener to be called back when the row is clicked or null if the row is non-clickable.

public Metadata getMetadata()

Returns the Metadata associated with the row or null if there is no metadata associated with the row.

public java.lang.CharSequence yourBoat()

Rows your boat.

Example usage:

 row.row().row().yourBoat(); // gently down the stream
 

public Row row()

Returns a Row for rowing Row.yourBoat()

public java.lang.String toString()

public int hashCode()

public boolean equals(java.lang.Object other)

Source

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

package androidx.car.app.model;

import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.car.app.model.Metadata.EMPTY_METADATA;

import static java.util.Objects.requireNonNull;

import android.annotation.SuppressLint;
import android.os.Looper;

import androidx.annotation.IntDef;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.car.app.annotations.CarProtocol;
import androidx.car.app.model.constraints.CarIconConstraints;
import androidx.car.app.model.constraints.CarTextConstraints;
import androidx.car.app.utils.CollectionUtils;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * Represents a row with a title, several lines of text, an optional image, and an optional action
 * or switch.
 */
@CarProtocol
public final class Row implements Item {
    /** A boat that belongs to you. */
    private static final String YOUR_BOAT = "\uD83D\uDEA3"; // 🚣

    /**
     * The type of images supported within rows.
     *
     * @hide
     */
    @RestrictTo(LIBRARY)
    @IntDef(value = {IMAGE_TYPE_SMALL, IMAGE_TYPE_ICON, IMAGE_TYPE_LARGE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface RowImageType {
    }

    /**
     * Represents a small image to be displayed in the row.
     *
     * <p>To minimize scaling artifacts across a wide range of car screens, apps should provide
     * images targeting a 88 x 88 dp bounding box. If necessary, the image will be scaled down while
     * preserving its aspect ratio.
     */
    public static final int IMAGE_TYPE_SMALL = (1 << 0);

    /**
     * Represents a large image to be displayed in the row.
     *
     * <p>To minimize scaling artifacts across a wide range of car screens, apps should provide
     * images targeting a 224 x 224 dp bounding box. If necessary, the image will be scaled down
     * while preserving its aspect ratio.
     */
    public static final int IMAGE_TYPE_LARGE = (1 << 1);

    /**
     * Represents a small image to be displayed in the row.
     *
     * <p>To minimize scaling artifacts across a wide range of car screens, apps should provide
     * images targeting a 88 x 88 dp bounding box. If necessary, the icon will be scaled down while
     * preserving its aspect ratio.
     *
     * <p>A tint color is expected to be provided via {@link CarIcon.Builder#setTint}. Otherwise, a
     * default tint color as determined by the host will be applied.
     */
    public static final int IMAGE_TYPE_ICON = (1 << 2);

    @Keep
    @Nullable
    private final CarText mTitle;
    @Keep
    private final List<CarText> mTexts;
    @Keep
    @Nullable
    private final CarIcon mImage;
    @Keep
    @Nullable
    private final Toggle mToggle;
    @Keep
    @Nullable
    private final OnClickDelegate mOnClickDelegate;
    @Keep
    private final Metadata mMetadata;
    @Keep
    private final boolean mIsBrowsable;
    @Keep
    @RowImageType
    private final int mRowImageType;

    /**
     * Returns the title of the row or {@code null} if not set.
     *
     * @see Builder#setTitle(CharSequence)
     */
    @Nullable
    public CarText getTitle() {
        return mTitle;
    }

    /**
     * Returns the list of text below the title.
     *
     * @see Builder#addText(CharSequence)
     */
    @NonNull
    public List<CarText> getTexts() {
        return CollectionUtils.emptyIfNull(mTexts);
    }

    /**
     * Returns the image to display in the row or {@code null} if the row does not contain an
     * image.
     *
     * @see Builder#setImage(CarIcon)
     * @see Builder#setImage(CarIcon, int)
     */
    @Nullable
    public CarIcon getImage() {
        return mImage;
    }

    /** Returns the type of the image in the row. */
    @RowImageType
    public int getRowImageType() {
        return mRowImageType;
    }

    /**
     * Returns the {@link Toggle} in the row or {@code null} if the row does not contain a
     * toggle.
     *
     * @see Builder#setToggle(Toggle)
     */
    @Nullable
    public Toggle getToggle() {
        return mToggle;
    }

    /**
     * Returns whether the row is browsable.
     *
     * <p>If a row is browsable, then no {@link Action} or {@link Toggle} can be added to it.
     *
     * @see Builder#isBrowsable()
     */
    public boolean isBrowsable() {
        return mIsBrowsable;
    }

    /**
     * Returns the {@link OnClickListener} to be called back when the row is clicked or {@code
     * null} if the row is non-clickable.
     */
    @Nullable
    public OnClickDelegate getOnClickDelegate() {
        return mOnClickDelegate;
    }

    /**
     * Returns the {@link Metadata} associated with the row or {@code null} if there is no
     * metadata associated with the row.
     */
    @Nullable
    public Metadata getMetadata() {
        return mMetadata;
    }

    /**
     * Rows your boat.
     *
     * <p>Example usage:
     *
     * <pre>{@code
     * row.row().row().yourBoat(); // gently down the stream
     * }</pre>
     */
    @NonNull
    public CharSequence yourBoat() {
        return YOUR_BOAT;
    }

    /** Returns a {@link Row} for rowing {@link #yourBoat()} */
    @NonNull
    public Row row() {
        return this;
    }

    @Override
    @NonNull
    public String toString() {
        return "[title: "
                + CarText.toShortString(mTitle)
                + ", text count: "
                + (mTexts != null ? mTexts.size() : 0)
                + ", image: "
                + mImage
                + ", isBrowsable: "
                + mIsBrowsable
                + "]";
    }

    @Override
    public int hashCode() {
        return Objects.hash(
                mTitle,
                mTexts,
                mImage,
                mToggle,
                mOnClickDelegate == null,
                mMetadata,
                mIsBrowsable,
                mRowImageType);
    }

    @Override
    public boolean equals(@Nullable Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof Row)) {
            return false;
        }
        Row otherRow = (Row) other;

        // Don't compare listener, only the fact whether it's present.
        return Objects.equals(mTitle, otherRow.mTitle)
                && Objects.equals(mTexts, otherRow.mTexts)
                && Objects.equals(mImage, otherRow.mImage)
                && Objects.equals(mToggle, otherRow.mToggle)
                && Objects.equals(mOnClickDelegate == null, otherRow.mOnClickDelegate == null)
                && Objects.equals(mMetadata, otherRow.mMetadata)
                && mIsBrowsable == otherRow.mIsBrowsable
                && mRowImageType == otherRow.mRowImageType;
    }

    Row(Builder builder) {
        mTitle = builder.mTitle;
        mTexts = CollectionUtils.unmodifiableCopy(builder.mTexts);
        mImage = builder.mImage;
        mToggle = builder.mToggle;
        mOnClickDelegate = builder.mOnClickDelegate;
        mMetadata = builder.mMetadata;
        mIsBrowsable = builder.mIsBrowsable;
        mRowImageType = builder.mRowImageType;
    }

    /** Constructs an empty instance, used by serialization code. */
    private Row() {
        mTitle = null;
        mTexts = Collections.emptyList();
        mImage = null;
        mToggle = null;
        mOnClickDelegate = null;
        mMetadata = EMPTY_METADATA;
        mIsBrowsable = false;
        mRowImageType = IMAGE_TYPE_SMALL;
    }

    /** A builder of {@link Row}. */
    public static final class Builder {
        @Nullable
        CarText mTitle;
        final List<CarText> mTexts = new ArrayList<>();
        @Nullable
        CarIcon mImage;
        @Nullable
        Toggle mToggle;
        @Nullable
        OnClickDelegate mOnClickDelegate;
        Metadata mMetadata = EMPTY_METADATA;
        boolean mIsBrowsable;
        @RowImageType
        int mRowImageType = IMAGE_TYPE_SMALL;

        /**
         * Sets the title of the row.
         *
         * <p>Only {@link DistanceSpan}s and {@link DurationSpan}s are supported in the input
         * string.
         *
         * @throws NullPointerException     if {@code title} is {@code null}
         * @throws IllegalArgumentException if {@code title} is empty, of if it contains
     *                                      unsupported spans
         */
        @NonNull
        public Builder setTitle(@NonNull CharSequence title) {
            CarText titleText = CarText.create(requireNonNull(title));
            if (titleText.isEmpty()) {
                throw new IllegalArgumentException("The title cannot be null or empty");
            }
            CarTextConstraints.TEXT_ONLY.validateOrThrow(titleText);
            mTitle = titleText;
            return this;
        }

        /**
         * Sets the title of the row, with support for multiple length variants.
         *
         * <p>Only {@link DistanceSpan}s and {@link DurationSpan}s are supported in the input
         * string.
         *
         * @throws NullPointerException     if {@code title} is {@code null}
         * @throws IllegalArgumentException if {@code title} is empty, of if it contains
     *                                      unsupported spans
         */
        @NonNull
        public Builder setTitle(@NonNull CarText title) {
            if (requireNonNull(title).isEmpty()) {
                throw new IllegalArgumentException("The title cannot be null or empty");
            }
            CarTextConstraints.TEXT_ONLY.validateOrThrow(title);
            mTitle = title;
            return this;
        }

        /**
         * Adds a text string to the row below the title.
         *
         * <p>The text can be customized with {@link ForegroundCarColorSpan},
         * {@link androidx.car.app.model.DistanceSpan}, and
         * {@link androidx.car.app.model.DurationSpan} instances, any other spans will be ignored
         * by the host.
         *
         * <p>Most templates allow up to 2 text strings, but this may vary. This limit is
         * documented in each individual template.
         *
         * <h4>Text Wrapping</h4>
         *
         * Each string added with this method will not wrap more than 1 line in the UI, with
         * one exception: if the template allows a maximum number of text strings larger than 1, and
         * the app adds a single text string, then this string will wrap up to the maximum.
         *
         * <p>For example, assuming 2 lines are allowed in the template where the row will be
         * used, this code:
         *
         * <pre>{@code
         * rowBuilder
         *     .addText("This is a rather long line of text")
         *     .addText("More text")
         * }</pre>
         *
         * <p>would wrap the text like this:
         *
         * <pre>
         * This is a rather long li...
         * More text
         * </pre>
         *
         * In contrast, this code:
         *
         * <pre>{@code
         * rowBuilder
         *     .addText("This is a rather long line of text. More text")
         * }</pre>
         *
         * <p>would wrap the single line of text at a maximum of 2 lines, producing a different
         * result:
         *
         * <pre>
         * This is a rather long line
         * of text. More text
         * </pre>
         *
         * <p>Note that when using a single line, a line break character can be used to break it
         * into two, but the results may be unpredictable depending on the width the text is
         * wrapped at:
         *
         * <pre>{@code
         * rowBuilder
         *     .addText("This is a rather long line of text\nMore text")
         * }</pre>
         *
         * <p>would produce a result that may loose the "More text" string:
         *
         * <pre>
         * This is a rather long line
         * of text
         * </pre>
         *
         * @throws NullPointerException     if {@code text} is {@code null}
         * @throws IllegalArgumentException if {@code text} contains unsupported spans
         * @see ForegroundCarColorSpan
         */
        @NonNull
        public Builder addText(@NonNull CharSequence text) {
            CarText carText = CarText.create(requireNonNull(text));
            CarTextConstraints.TEXT_WITH_COLORS.validateOrThrow(carText);
            mTexts.add(CarText.create(requireNonNull(text)));
            return this;
        }

        /**
         * Adds a text string to the row below the title, with support for multiple length variants.
         *
         * @throws NullPointerException     if {@code text} is {@code null}
         * @throws IllegalArgumentException if {@code text} contains unsupported spans
         * @see Builder#addText(CharSequence)
         */
        @NonNull
        public Builder addText(@NonNull CarText text) {
            CarTextConstraints.TEXT_WITH_COLORS.validateOrThrow(requireNonNull(text));
            mTexts.add(text);
            return this;
        }

        /**
         * Sets an image to show in the row with the default size {@link #IMAGE_TYPE_SMALL}.
         *
         * @throws NullPointerException if {@code image} is {@code null}
         * @see #setImage(CarIcon, int)
         */
        @NonNull
        public Builder setImage(@NonNull CarIcon image) {
            return setImage(requireNonNull(image), IMAGE_TYPE_SMALL);
        }

        /**
         * Sets an image to show in the row with the given image type.
         *
         * <p>For a custom {@link CarIcon}, its {@link androidx.core.graphics.drawable.IconCompat}
         * instance can be of {@link androidx.core.graphics.drawable.IconCompat#TYPE_BITMAP},
         * {@link androidx.core.graphics.drawable.IconCompat#TYPE_RESOURCE}, or
         * {@link androidx.core.graphics.drawable.IconCompat#TYPE_URI}.
         *
         * <h4>Image Sizing Guidance</h4>
         *
         * <p>If the input image's size exceeds the sizing requirements for the given image type in
         * either one of the dimensions, it will be scaled down to be centered inside the
         * bounding box while preserving its aspect ratio.
         *
         * <p>See {@link CarIcon} for more details related to providing icon and image resources
         * that work with different car screen pixel densities.
         *
         * @param image     the {@link CarIcon} to display or {@code null} to not display one
         * @param imageType one of {@link #IMAGE_TYPE_ICON}, {@link #IMAGE_TYPE_SMALL} or {@link
         *                  #IMAGE_TYPE_LARGE}
         * @throws NullPointerException if {@code image} is {@code null}
         */
        @NonNull
        public Builder setImage(@NonNull CarIcon image, @RowImageType int imageType) {
            CarIconConstraints.UNCONSTRAINED.validateOrThrow(requireNonNull(image));
            mImage = image;
            mRowImageType = imageType;
            return this;
        }

        /**
         * Sets a {@link Toggle} to show in the row.
         *
         * @throws NullPointerException if {@code toggle} is {@code null}
         */
        @NonNull
        public Builder setToggle(@NonNull Toggle toggle) {
            mToggle = requireNonNull(toggle);
            return this;
        }

        /**
         * Shows an icon at the end of the row that indicates that the row is browsable.
         *
         * <p>Browsable rows can be used, for example, to represent the parent row in a hierarchy of
         * lists with child lists.
         *
         * <p>If a row is browsable, then no {@link Action} or {@link Toggle} can be added to it.
         */
        @NonNull
        public Builder setBrowsable(boolean isBrowsable) {
            mIsBrowsable = isBrowsable;
            return this;
        }

        /**
         * Sets the {@link OnClickListener} to be called back when the row is clicked.
         *
         * <p>Note that the listener relates to UI events and will be executed on the main thread
         * using {@link Looper#getMainLooper()}.
         *
         * @throws NullPointerException if {@code onClickListener} is {@code null}
         */
        @NonNull
        @SuppressLint({"MissingGetterMatchingBuilder", "ExecutorRegistration"})
        public Builder setOnClickListener(@NonNull OnClickListener onClickListener) {
            mOnClickDelegate = OnClickDelegateImpl.create(onClickListener);
            return this;
        }


        /**
         * Sets the {@link Metadata} associated with the row.
         *
         * @param metadata The metadata to set with the row. Pass {@link Metadata#EMPTY_METADATA}
         *                 to not associate any metadata with the row
         */
        @NonNull
        public Builder setMetadata(@NonNull Metadata metadata) {
            mMetadata = metadata;
            return this;
        }

        /**
         * Constructs the {@link Row} defined by this builder.
         *
         * @throws IllegalStateException if the row's title is not set, if it is a browsable
         *                               row and has a {@link Toggle}, if it is a browsable
         *                               row but does not have a {@link OnClickListener}, or if
         *                               it has both a {@link OnClickListener} and a {@link
         *                               Toggle}
         */
        @NonNull
        public Row build() {
            if (mTitle == null) {
                throw new IllegalStateException("A title must be set on the row");
            }

            if (mIsBrowsable) {
                if (mToggle != null) {
                    throw new IllegalStateException("A browsable row must not have a toggle set");
                }
                if (mOnClickDelegate == null) {
                    throw new IllegalStateException(
                            "A browsable row must have its onClickListener set");
                }
            }

            if (mToggle != null && mOnClickDelegate != null) {
                throw new IllegalStateException(
                        "If a row contains a toggle, it must not have a onClickListener set");
            }

            return new Row(this);
        }

        /** Returns an empty {@link Builder} instance. */
        public Builder() {
        }
    }
}