PlaceMarker.java
/*
* 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 java.util.Objects.requireNonNull;
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.CarColorConstraints;
import androidx.car.app.model.constraints.CarIconConstraints;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/** Describes how a place is to be displayed on a map. */
@CarProtocol
public final class PlaceMarker {
/**
* Describes the type of image a marker icon represents.
*
* @hide
*/
@IntDef(value = {TYPE_ICON, TYPE_IMAGE})
@Retention(RetentionPolicy.SOURCE)
@RestrictTo(LIBRARY)
public @interface MarkerIconType {
}
/**
* Represents a marker icon.
*
* <p>To minimize scaling artifacts across a wide range of car screens, apps should provide
* images targeting a 64 x 64 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 TYPE_ICON = 0;
/**
* Represents a marker image.
*
* <p>To minimize scaling artifacts across a wide range of car screens, apps should provide
* images targeting a 72 x 72 dp bounding box. If necessary, the image will be scaled down while
* preserving its aspect ratio.
*/
public static final int TYPE_IMAGE = 1;
private static final int MAX_LABEL_LENGTH = 3;
@Keep
@Nullable
private final CarIcon mIcon;
@Keep
@Nullable
private final CarText mLabel;
@Keep
@Nullable
private final CarColor mColor;
@Keep
@MarkerIconType
private final int mIconType;
/**
* Returns the {@link CarIcon} associated with this marker or {@code null} if not set.
*/
@Nullable
public CarIcon getIcon() {
return mIcon;
}
/**
* Returns the type of icon used with this marker.
*/
@MarkerIconType
public int getIconType() {
return mIconType;
}
/**
* Returns the text that should be rendered as the marker's content or {@code null} if one
* is not set.
*
* <p>Note that a {@link PlaceMarker} can only display either an icon or a text label. If
* both are set, then {@link #getIcon()} will take precedence.
*/
@Nullable
public CarText getLabel() {
return mLabel;
}
/**
* Returns the marker color or {@code null} if not set.
*
* <p>See {@link Builder#setColor} on rules related to how the color is applied.
*/
@Nullable
public CarColor getColor() {
return mColor;
}
@NonNull
@Override
public String toString() {
return "["
+ (mIcon != null
? mIcon.toString()
: mLabel != null ? CarText.toShortString(mLabel) : super.toString())
+ "]";
}
@Override
public int hashCode() {
return Objects.hash(mIcon, mLabel, mColor, mIconType);
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PlaceMarker)) {
return false;
}
PlaceMarker otherMarker = (PlaceMarker) other;
return Objects.equals(mIcon, otherMarker.mIcon)
&& Objects.equals(mLabel, otherMarker.mLabel)
&& Objects.equals(mColor, otherMarker.mColor)
&& mIconType == otherMarker.mIconType;
}
PlaceMarker(@NonNull Builder builder) {
mIcon = builder.mIcon;
mIconType = builder.mIconType;
mLabel = builder.mLabel;
mColor = builder.mColor;
}
/** Private empty constructor used by serialization code. */
private PlaceMarker() {
mIcon = null;
mIconType = TYPE_ICON;
mLabel = null;
mColor = null;
}
/** A builder of {@link PlaceMarker}. */
public static final class Builder {
@Nullable
CarIcon mIcon;
@Nullable
CarText mLabel;
@Nullable
CarColor mColor;
@MarkerIconType
int mIconType = TYPE_ICON;
/**
* Sets the icon to display in the marker.
*
* <p>Unless set with this method, the marker will not have an icon.
*
* <p>If a label is specified with {@link #setLabel}, the icon will take precedence over it.
*
* <h4>Icon Sizing Guidance</h4>
*
* If the input icon's size exceeds the sizing requirements for the given icon 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 icon the {@link CarIcon} to display inside the marker
* @param iconType one of {@link #TYPE_ICON} or {@link #TYPE_IMAGE}
* @throws NullPointerException if the {@code icon} is {@code null}
*/
@NonNull
public Builder setIcon(@NonNull CarIcon icon, @MarkerIconType int iconType) {
CarIconConstraints.DEFAULT.validateOrThrow(requireNonNull(icon));
mIcon = icon;
mIconType = iconType;
return this;
}
/**
* Sets the text that should be displayed as the marker's content.
*
* <p>Unless set with this method, the marker will not have a label.
*
* <p>If an icon is specified with {@link #setIcon}, the icon will take precedence.
*
* <p>Spans are not supported in the input string and will be ignored.
*
* @param label the text to display inside of the marker. The string must have a maximum
* size of 3 characters. Set to {@code null} to let the host choose a
* labelling scheme (for example, using a sequence of numbers)
* @throws NullPointerException if the {@code label} is {@code null}
* @see CarText
*/
@NonNull
public Builder setLabel(@NonNull CharSequence label) {
if (requireNonNull(label).length() > MAX_LABEL_LENGTH) {
throw new IllegalArgumentException(
"Marker label cannot contain more than " + MAX_LABEL_LENGTH
+ " characters");
}
mLabel = CarText.create(label);
return this;
}
/**
* Sets the color that should be used for the marker on the map.
*
* <p>This color is applied in the following cases:
*
* <ul>
* <li>When the {@link PlaceMarker} is displayed on the map, the pin enclosing the icon or
* label will be painted using the given color.
* <li>When the {@link PlaceMarker} is displayed on the list, the color will be applied
* if the content is a label. A label rendered inside a map's pin cannot be colored
* and will always use the default color as chosen by the host.
* </ul>
*
* <p>Unless set with this method, the host will use a default color for the marker.
*
* <p>The host may ignore this color and use the default instead if the color does not
* pass the contrast requirements.
*
* <p>A color cannot be set if the marker's icon type is of {@link #TYPE_IMAGE}.
*
* @throws NullPointerException if the {@code color} is {@code null}
*/
@NonNull
public Builder setColor(@NonNull CarColor color) {
CarColorConstraints.UNCONSTRAINED.validateOrThrow(requireNonNull(color));
mColor = color;
return this;
}
/**
* Constructs the {@link PlaceMarker} defined by this builder.
*
* @throws IllegalStateException if the icon is of the type {@link #TYPE_IMAGE} and a a
* color is set
*/
@NonNull
public PlaceMarker build() {
if (mColor != null && (mIcon != null && mIconType == TYPE_IMAGE)) {
throw new IllegalStateException("Color cannot be set for icon set with TYPE_IMAGE");
}
return new PlaceMarker(this);
}
/** Returns an empty {@link Builder} instance. */
public Builder() {
}
}
}