CarMessage.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.messaging.model;
import static androidx.car.app.messaging.model.ConversationItem.validateSender;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.app.annotations.CarProtocol;
import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.KeepFields;
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.model.CarText;
import androidx.core.app.Person;
import java.util.Objects;
/** Represents a single message in a {@link ConversationItem} */
@ExperimentalCarApi
@CarProtocol
@RequiresCarApi(7)
@KeepFields
public class CarMessage {
@Nullable
private final Bundle mSender;
@Nullable
private final CarText mBody;
@Nullable
private final String mMultimediaMimeType;
@Nullable
private final Uri mMultimediaUri;
private final long mReceivedTimeEpochMillis;
private final boolean mIsRead;
@Override
public int hashCode() {
return Objects.hash(
PersonsEqualityHelper.getPersonHashCode(getSender()),
mBody,
mReceivedTimeEpochMillis,
mIsRead
);
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof CarMessage)) {
return false;
}
CarMessage otherCarMessage = (CarMessage) other;
return
PersonsEqualityHelper.arePersonsEqual(getSender(), otherCarMessage.getSender())
&& Objects.equals(mBody, otherCarMessage.mBody)
&& mReceivedTimeEpochMillis == otherCarMessage.mReceivedTimeEpochMillis
&& mIsRead == otherCarMessage.mIsRead;
}
CarMessage(@NonNull Builder builder) {
this.mSender = builder.mSender == null ? null : validateSender(builder.mSender).toBundle();
this.mBody = builder.mBody;
this.mMultimediaMimeType = builder.mMultimediaMimeType;
this.mMultimediaUri = builder.mMultimediaUri;
this.mReceivedTimeEpochMillis = builder.mReceivedTimeEpochMillis;
this.mIsRead = builder.mIsRead;
}
/** Default constructor for serialization. */
private CarMessage() {
this.mSender = null;
this.mBody = null;
this.mMultimediaMimeType = null;
this.mMultimediaUri = null;
this.mReceivedTimeEpochMillis = 0;
this.mIsRead = false;
}
/**
* Returns a {@link Person} representing the message sender.
*
* <p> For self-sent messages, this method will return {@code null} or
* {@link ConversationItem#getSelf()}.
*/
@Nullable
public Person getSender() {
return mSender == null ? null : Person.fromBundle(mSender);
}
/**
* Returns a {@link CarText} representing the message body
*
* <p> Messages must have one or both of the following:
* <ul>
* <li> A message body (text)
* <li> A MIME type + URI (image, audio, etc.)
* </ul>
*
* @see #getMultimediaMimeType()
* @see #getMultimediaUri()
*/
@Nullable
public CarText getBody() {
return mBody;
}
/**
* Returns a {@link String} representing the MIME type of a multimedia message
*
* <p> Messages must have one or both of the following:
* <ul>
* <li> A message body (text)
* <li> A MIME type + URI (image, audio, etc.)
* </ul>
*
* @see #getBody()
* @see #getMultimediaUri()
*/
@Nullable
public String getMultimediaMimeType() {
return mMultimediaMimeType;
}
/**
* Returns a {@link Uri} pointing to the contents of a multimedia message.
*
* <p> Messages must have one or both of the following:
* <ul>
* <li> A message body (text)
* <li> A MIME type + URI (image, audio, etc.)
* </ul>
*
* @see #getBody()
* @see #getMultimediaMimeType()
*/
@Nullable
public Uri getMultimediaUri() {
return mMultimediaUri;
}
/** Returns a {@code long} representing the message timestamp (in epoch millis) */
public long getReceivedTimeEpochMillis() {
return mReceivedTimeEpochMillis;
}
/** Returns a {@code boolean}, indicating whether the message has been read */
public boolean isRead() {
return mIsRead;
}
/** A builder for {@link CarMessage} */
public static final class Builder {
@Nullable
Person mSender;
@Nullable
CarText mBody;
@Nullable
String mMultimediaMimeType;
@Nullable
Uri mMultimediaUri;
long mReceivedTimeEpochMillis;
boolean mIsRead;
/**
* Sets a {@link Person} representing the message sender
*
* <p> The {@link Person} must specify a non-null
* {@link Person.Builder#setName(CharSequence)} and
* {@link Person.Builder#setKey(String)}.
*/
public @NonNull Builder setSender(@Nullable Person sender) {
mSender = sender;
return this;
}
/**
* Sets a {@link CarText} representing the message body
*
* <p> Messages must have one or both of the following:
* <ul>
* <li> A message body (text)
* <li> A MIME type + URI (image, audio, etc.)
* </ul>
*
* @see #setMultimediaMimeType(String)
* @see #setMultimediaUri(Uri)
*/
public @NonNull Builder setBody(@Nullable CarText body) {
mBody = body;
return this;
}
/**
* Sets a {@link String} representing the MIME type of a multimedia message
*
* <p> Messages must have one or both of the following:
* <ul>
* <li> A message body (text)
* <li> A MIME type + URI (image, audio, etc.)
* </ul>
*
* @see #setBody(CarText)
* @see #setMultimediaUri(Uri)
*/
public @NonNull Builder setMultimediaMimeType(@Nullable String multimediaMimeType) {
this.mMultimediaMimeType = multimediaMimeType;
return this;
}
/**
* Sets a {@link Uri} pointing to the contents of a multimedia message.
*
* <p> Messages must have one or both of the following:
* <ul>
* <li> A message body (text)
* <li> A MIME type + URI (image, audio, etc.)
* </ul>
*
* @see #setBody(CarText)
* @see #setMultimediaMimeType(String)
*/
public @NonNull Builder setMultimediaUri(@Nullable Uri multimediaUri) {
this.mMultimediaUri = multimediaUri;
return this;
}
/** Sets a {@code long} representing the message timestamp (in epoch millis) */
public @NonNull Builder setReceivedTimeEpochMillis(long receivedTimeEpochMillis) {
mReceivedTimeEpochMillis = receivedTimeEpochMillis;
return this;
}
/** Sets a {@code boolean}, indicating whether the message has been read */
public @NonNull Builder setRead(boolean isRead) {
mIsRead = isRead;
return this;
}
/** Returns a new {@link CarMessage} instance defined by this builder */
public @NonNull CarMessage build() {
if (mMultimediaMimeType == null ^ mMultimediaUri == null) {
throw new IllegalStateException("Incomplete multimedia data detected in "
+ "CarMessage. Please be sure to provide both MIME type and URI for "
+ "multimedia messages.");
}
// Conceptually, we're checking that body text and multimedia data (mime type or URI)
// are null.
// The compiler complains if I check both mime type and URI, due to previous validation.
if (mBody == null && mMultimediaMimeType == null) {
throw new IllegalStateException("Message must have content. Please provide body "
+ "text, multimedia data (URI + MIME type), or both.");
}
return new CarMessage(this);
}
}
}