Alert.java
/*
* 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.car.app.model;
import static java.util.Objects.requireNonNull;
import android.annotation.SuppressLint;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.app.annotations.CarProtocol;
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.model.constraints.CarIconConstraints;
import androidx.car.app.utils.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Represents an alert with an optional icon, subtitle and actions.
*/
@CarProtocol
@RequiresCarApi(5)
public final class Alert {
/* Maximum number of actions allowed on the alert. */
private static final int MAX_ACTION_COUNT = 2;
@Keep
private final int mId;
@Keep
@Nullable
private final CarIcon mIcon;
@Keep
@NonNull
private final CarText mTitle;
@Keep
@Nullable
private final CarText mSubtitle;
@Keep
@NonNull
private final List<Action> mActions;
@Keep
private final long mDuration;
@Keep
@Nullable
private final AlertCallbackDelegate mCallbackDelegate;
/** Returns the id of the alert. */
public int getId() {
return mId;
}
/** Returns the title displayed in the alert. */
@NonNull
public CarText getTitle() {
return mTitle;
}
/**
* Returns the subtitle displayed in the alert or {@code null} if the alert does
* not have a subtitle.
*
* @see Builder#setSubtitle(CarText)
*/
@Nullable
public CarText getSubtitle() {
return mSubtitle;
}
/**
* Returns the {@link CarIcon} to display in the alert or {@code null} if the alert does
* not have an icon.
*
* @see Builder#setIcon(CarIcon)
*/
@Nullable
public CarIcon getIcon() {
return mIcon;
}
/**
* Returns the {@link List} of {@link Action}s associated with the alert.
*
* @see Builder#addAction(Action)
*/
@NonNull
public List<Action> getActions() {
return mActions;
}
/** Returns the maximum duration in milli seconds for which the alert can be shown. */
public long getDurationMillis() {
return mDuration;
}
/**
* Returns the {@link AlertCallbackDelegate} that should be used for this alert.
*
* @see Builder#setCallback(AlertCallbackDelegate)
*/
@Nullable
public AlertCallbackDelegate getCallbackDelegate() {
return mCallbackDelegate;
}
@Override
@NonNull
public String toString() {
return "[id: " + mId + ", title: " + mTitle + ", icon: " + mIcon + "]";
}
Alert(Builder builder) {
mId = builder.mId;
mTitle = builder.mTitle;
mSubtitle = builder.mSubtitle;
mIcon = builder.mIcon;
mActions = CollectionUtils.unmodifiableCopy(builder.mActions);
mDuration = builder.mDuration;
mCallbackDelegate = builder.mCallbackDelegate;
}
/** Constructs an empty instance, used by serialization code. */
private Alert() {
mId = 0;
mTitle = CarText.create("");
mSubtitle = null;
mIcon = null;
mActions = new ArrayList<>();
mDuration = 0;
mCallbackDelegate = null;
}
@Override
public int hashCode() {
return Objects.hash(mId);
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Alert)) {
return false;
}
Alert otherAlert = (Alert) other;
// Only compare the alertIds.
return mId == otherAlert.mId;
}
/** A builder of {@link Alert}. */
public static final class Builder {
int mId;
@NonNull
CarText mTitle;
@Nullable
CarText mSubtitle;
@Nullable
CarIcon mIcon;
@NonNull
List<Action> mActions;
long mDuration;
@Nullable
AlertCallbackDelegate mCallbackDelegate;
/**
* Creates an {@link Builder} instance.
*
* <p>Text spans are supported.
*
* @param alertId The unique identifier used for the alert. Alerts with the same id are
* considered equal.
* @param title The title of the alert.
* @param durationMillis The maximum duration the alert can be shown in milli seconds.
* @throws NullPointerException if {@code title} is {@code null}
* @throws IllegalArgumentException if {@code duration} is not positive
* @see CarText
*/
public Builder(int alertId, @NonNull CarText title, long durationMillis) {
if (durationMillis <= 0) {
throw new IllegalArgumentException("Duration should be a positive number.");
}
mId = alertId;
mTitle = requireNonNull(title);
mDuration = durationMillis;
mActions = new ArrayList<>(MAX_ACTION_COUNT);
}
/**
* Sets the subtitle to display in the alert, with support for multiple length variants.
*
* <p>Text spans are supported.
*
* @throws NullPointerException if {@code subtitle} is {@code null}
* @see CarText
*/
@NonNull
public Builder setSubtitle(@NonNull CarText subtitle) {
mSubtitle = requireNonNull(subtitle);
return this;
}
/**
* Sets the icon to display in the alert.
*
* <h4>Icon Sizing Guidance</h4>
*
* To minimize scaling artifacts across a wide range of car screens, apps should provide
* icons targeting a 88 x 88 dp bounding box. If the icon 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.
*
* <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 icon} is {@code null}
*/
@NonNull
public Builder setIcon(@NonNull CarIcon icon) {
CarIconConstraints.DEFAULT.validateOrThrow(requireNonNull(icon));
mIcon = icon;
return this;
}
/**
* Adds the {@code action} to the list of actions on the alert.
*
* <p>An alert can have up to 2 actions.
*
* @throws IllegalStateException if more than 2 actions are added.
*/
@NonNull
public Builder addAction(@NonNull Action action) {
if (mActions.size() >= MAX_ACTION_COUNT) {
throw new IllegalStateException("Cannot add more than " + MAX_ACTION_COUNT
+ " actions.");
}
mActions.add(action);
return this;
}
/**
* Sets the {@link AlertCallback} to receive alert related events.
*
* @throws NullPointerException if {@code callback} is {@code null}
*/
@NonNull
@SuppressLint({"MissingGetterMatchingBuilder", "ExecutorRegistration"})
public Builder setCallback(@NonNull AlertCallback callback) {
mCallbackDelegate = AlertCallbackDelegateImpl.create(requireNonNull(callback));
return this;
}
/** Constructs the {@link Alert} defined by this builder. */
@NonNull
public Alert build() {
return new Alert(this);
}
}
}