RoutingInfo.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.navigation.model;
import static java.util.Objects.requireNonNull;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.app.annotations.CarProtocol;
import androidx.car.app.model.CarIcon;
import androidx.car.app.model.Distance;
import androidx.car.app.model.constraints.CarIconConstraints;
import androidx.car.app.navigation.model.NavigationTemplate.NavigationInfo;
import java.util.Objects;
/**
* Represents routing information that can be shown in the {@link NavigationTemplate} during
* navigation
*/
@CarProtocol
public final class RoutingInfo implements NavigationInfo {
@Keep
@Nullable
private final Step mCurrentStep;
@Keep
@Nullable
private final Distance mCurrentDistance;
@Keep
@Nullable
private final Step mNextStep;
@Keep
@Nullable
private final CarIcon mJunctionImage;
@Keep
private final boolean mIsLoading;
/**
* Returns whether the routing info is in a loading state.
*
* @see Builder#setLoading(boolean)
*/
public boolean isLoading() {
return mIsLoading;
}
/**
* Returns the current step to display to the user or {@code null} if not set.
*
* @see Builder#setCurrentStep(Step, Distance)
*/
@Nullable
public Step getCurrentStep() {
return mCurrentStep;
}
/**
* Returns the current distance to display to the user or {@code null} if not set.
*
* @see Builder#setCurrentStep(Step, Distance)
*/
@Nullable
public Distance getCurrentDistance() {
return mCurrentDistance;
}
@Nullable
public Step getNextStep() {
return mNextStep;
}
/**
* Returns an image for a junction of the maneuver or {@code null} if not set.
*
* @see Builder#setJunctionImage(CarIcon)
*/
@Nullable
public CarIcon getJunctionImage() {
return mJunctionImage;
}
@NonNull
@Override
public String toString() {
return "RoutingInfo";
}
@Override
public int hashCode() {
return Objects.hash(mCurrentStep, mCurrentDistance, mNextStep, mJunctionImage, mIsLoading);
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof RoutingInfo)) {
return false;
}
RoutingInfo otherInfo = (RoutingInfo) other;
return mIsLoading == otherInfo.mIsLoading
&& Objects.equals(mCurrentStep, otherInfo.mCurrentStep)
&& Objects.equals(mCurrentDistance, otherInfo.mCurrentDistance)
&& Objects.equals(mNextStep, otherInfo.mNextStep)
&& Objects.equals(mJunctionImage, otherInfo.mJunctionImage);
}
RoutingInfo(Builder builder) {
mCurrentStep = builder.mCurrentStep;
mCurrentDistance = builder.mCurrentDistance;
mNextStep = builder.mNextStep;
mJunctionImage = builder.mJunctionImage;
mIsLoading = builder.mIsLoading;
}
/** Constructs an empty instance, used by serialization code. */
private RoutingInfo() {
mCurrentStep = null;
mCurrentDistance = null;
mNextStep = null;
mJunctionImage = null;
mIsLoading = false;
}
/** A builder of {@link RoutingInfo}. */
public static final class Builder {
@Nullable
Step mCurrentStep;
@Nullable
Distance mCurrentDistance;
@Nullable
Step mNextStep;
@Nullable
CarIcon mJunctionImage;
boolean mIsLoading;
/**
* Sets the current {@link Step} and {@link Distance} to display in the template.
*
* <p>A {@link Step} with a {@link Maneuver} of type {@link Maneuver#TYPE_UNKNOWN} will
* shown here with the given icon.
*
* <h4>Image Sizing Guidance</h4>
*
* Images in the cue of the {@link Step} object, set with {@link Step.Builder#setCue}, can
* contain image spans. To minimize scaling artifacts across a wide range of car screens,
* apps should provide images targeting a 216 x 72 dp bounding box. If necessary, those
* images in the spans will be scaled down to fit the bounding box while preserving their
* aspect ratios.
*
* <p>See {@link CarIcon} for more details related to providing icon and image resources
* that work with different car screen pixel densities.
*
* @throws NullPointerException if {@code currentStep} is {@code null}
* @throws NullPointerException if {@code currentDistance} is {@code null}
*/
@NonNull
public Builder setCurrentStep(@NonNull Step currentStep,
@NonNull Distance currentDistance) {
mCurrentStep = requireNonNull(currentStep);
mCurrentDistance = requireNonNull(currentDistance);
return this;
}
/**
* Sets the next {@link Step}.
*
* <p>Unless set with this method, the next step won't be displayed.
*
* <h4>Image Sizing Guidance</h4>
*
* Images in the cue of the {@link Step} object, set with {@link Step.Builder#setCue}, can
* contain image spans. To minimize scaling artifacts across a wide range of car screens,
* apps should provide images targeting a 216 x 72 dp bounding box. If necessary, those
* images in the spans will be scaled down to fit the bounding box while preserving their
* aspect ratios.
*
* <p>See {@link CarIcon} for more details related to providing icon and image resources
* that work with different car screen pixel densities.
*
* @throws NullPointerException if {@code nextStep} is {@code null}
*/
@NonNull
public Builder setNextStep(@NonNull Step nextStep) {
mNextStep = requireNonNull(nextStep);
return this;
}
/**
* Sets an image of a junction for the maneuver.
*
* <p>For example, a photo-realistic view of the upcoming junction that the driver can
* see when executing the maneuver.
*
* <h4>Image Sizing Guidance</h4>
*
* To minimize scaling artifacts across a wide range of car screens, apps should provide
* images targeting a 500 x 312 dp bounding box. If the image exceeds this maximum size in
* either one of the dimensions, it will be scaled down to be centered inside the
* bounding box while preserving its aspect ratio. The aspect ratio should be greater than
* or equal to 1.6 in order to fit the horizontal space fully.
*
* <p>On smaller screens the junction image may result in the hiding of the {@link Lane}
* s, {@link TravelEstimate} or next {@link Step}.
*
* <p>See {@link CarIcon} for more details related to providing icon and image resources
* that work with different car screen pixel densities.
*
* @throws NullPointerException if {@code junctionImage} is {@code null}
*/
@NonNull
public Builder setJunctionImage(@NonNull CarIcon junctionImage) {
CarIconConstraints.DEFAULT.validateOrThrow(requireNonNull(junctionImage));
mJunctionImage = junctionImage;
return this;
}
/**
* Sets whether the {@link RoutingInfo} is in a loading state.
*
* <p>If set to {@code true}, the UI will show a loading indicator, and adding any other
* routing info will throw an {@link IllegalArgumentException}. The caller is expected to
* call {@link androidx.car.app.Screen#invalidate()} and send the new template content
* to the host once the data is ready. If set to {@code false}, the UI shows the actual
* routing info.
*/
@NonNull
public Builder setLoading(boolean isLoading) {
mIsLoading = isLoading;
return this;
}
/**
* Constructs the {@link RoutingInfo} defined by this builder.
*
* <h4>Requirements</h4>
*
* The {@link RoutingInfo} can be in a loading state by passing {@code true} to {@link
* #setLoading(boolean)}, in which case no other fields may be set. Otherwise, the current
* step and distance must be set. If the lane information is set with {@link
* Step.Builder#addLane(Lane)}, then the lane image must also be set with {@link
* Step.Builder#setLanesImage(CarIcon)}.
*
* @throws IllegalStateException if the {@link RoutingInfo} does not meet the template's
* requirements
*/
@NonNull
public RoutingInfo build() {
Step current = mCurrentStep;
Distance distance = mCurrentDistance;
if (mIsLoading) {
if (current != null || distance != null || mNextStep != null
|| mJunctionImage != null) {
throw new IllegalStateException(
"The routing info is set to loading but is not empty");
}
} else {
if (current == null || distance == null) {
throw new IllegalStateException(
"Current step and distance must be set during the navigating state");
}
if (!current.getLanes().isEmpty() && current.getLanesImage() == null) {
// TODO(b/154660041): Remove restriction when lane image can be draw from
// lane info.
throw new IllegalStateException(
"Current step must have a lanes image if the lane information is set");
}
}
return new RoutingInfo(this);
}
/** Constructs an empty {@link Builder} instance. */
public Builder() {
}
}
}