Add structured messaging data for A4C messaging apps
Bug: 246999288
Test: ./gradlew :car:app:app:test
Relnote: "Add generic messaging data representations to A4C"
Change-Id: I366cce4d1b5af497b0ef65dec7cde45344764a54
diff --git a/car/app/app/api/public_plus_experimental_1.3.0-beta02.txt b/car/app/app/api/public_plus_experimental_1.3.0-beta02.txt
index 108129b..514019a 100644
--- a/car/app/app/api/public_plus_experimental_1.3.0-beta02.txt
+++ b/car/app/app/api/public_plus_experimental_1.3.0-beta02.txt
@@ -841,6 +841,44 @@
}
+package androidx.car.app.messaging.model {
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public class CarMessage {
+ method public androidx.car.app.model.CarText getBody();
+ method public long getReceivedTimeEpochMillis();
+ method public androidx.core.app.Person getSender();
+ method public boolean isRead();
+ }
+
+ public static final class CarMessage.Builder {
+ ctor public CarMessage.Builder();
+ method public androidx.car.app.messaging.model.CarMessage build();
+ method public androidx.car.app.messaging.model.CarMessage.Builder setBody(androidx.car.app.model.CarText);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setRead(boolean);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setReceivedTimeEpochMillis(long);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setSender(androidx.core.app.Person);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public class ConversationItem implements androidx.car.app.model.Item {
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public String getId();
+ method public java.util.List<androidx.car.app.messaging.model.CarMessage!> getMessages();
+ method public androidx.car.app.model.CarText getTitle();
+ method public boolean isGroupConversation();
+ }
+
+ public static final class ConversationItem.Builder {
+ ctor public ConversationItem.Builder();
+ method public androidx.car.app.messaging.model.ConversationItem build();
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setGroupConversation(boolean);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setId(String);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setMessages(java.util.List<androidx.car.app.messaging.model.CarMessage!>);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setTitle(androidx.car.app.model.CarText);
+ }
+
+}
+
package androidx.car.app.model {
@androidx.car.app.annotations.CarProtocol public final class Action {
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 108129b..514019a 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -841,6 +841,44 @@
}
+package androidx.car.app.messaging.model {
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public class CarMessage {
+ method public androidx.car.app.model.CarText getBody();
+ method public long getReceivedTimeEpochMillis();
+ method public androidx.core.app.Person getSender();
+ method public boolean isRead();
+ }
+
+ public static final class CarMessage.Builder {
+ ctor public CarMessage.Builder();
+ method public androidx.car.app.messaging.model.CarMessage build();
+ method public androidx.car.app.messaging.model.CarMessage.Builder setBody(androidx.car.app.model.CarText);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setRead(boolean);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setReceivedTimeEpochMillis(long);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setSender(androidx.core.app.Person);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public class ConversationItem implements androidx.car.app.model.Item {
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public String getId();
+ method public java.util.List<androidx.car.app.messaging.model.CarMessage!> getMessages();
+ method public androidx.car.app.model.CarText getTitle();
+ method public boolean isGroupConversation();
+ }
+
+ public static final class ConversationItem.Builder {
+ ctor public ConversationItem.Builder();
+ method public androidx.car.app.messaging.model.ConversationItem build();
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setGroupConversation(boolean);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setId(String);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setMessages(java.util.List<androidx.car.app.messaging.model.CarMessage!>);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setTitle(androidx.car.app.model.CarText);
+ }
+
+}
+
package androidx.car.app.model {
@androidx.car.app.annotations.CarProtocol public final class Action {
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java
new file mode 100644
index 0000000..f5bd59f
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java
@@ -0,0 +1,123 @@
+/*
+ * 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 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.annotations.ExperimentalCarApi;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.model.CarText;
+import androidx.core.app.Person;
+
+/** Represents a single message in a {@link ConversationItem} */
+@ExperimentalCarApi
+@CarProtocol
+@RequiresCarApi(6)
+public class CarMessage {
+ @Keep
+ @NonNull
+ private final Person mSender;
+
+ @Keep
+ @NonNull
+ private final CarText mBody;
+ @Keep
+ private final long mReceivedTimeEpochMillis;
+
+ @Keep
+ private final boolean mIsRead;
+
+ CarMessage(@NonNull Builder builder) {
+ this.mSender = requireNonNull(builder.mSender);
+ this.mBody = requireNonNull(builder.mBody);
+ this.mReceivedTimeEpochMillis = builder.mReceivedTimeEpochMillis;
+ this.mIsRead = builder.mIsRead;
+ }
+
+ /** Default constructor for serialization. */
+ private CarMessage() {
+ this.mSender = new Person.Builder().setName("").build();
+ this.mBody = new CarText.Builder("").build();
+ this.mReceivedTimeEpochMillis = 0;
+ this.mIsRead = false;
+ }
+
+
+ /** Returns a {@link Person} representing the message sender */
+ @NonNull public Person getSender() {
+ return mSender;
+ }
+
+ /** Returns a {@link CarText} representing the message body */
+ @NonNull
+ public CarText getBody() {
+ return mBody;
+ }
+
+ /** Returns a {@code long} representing the message timestamp (in epoch millis) */
+ public long getReceivedTimeEpochMillis() {
+ return mReceivedTimeEpochMillis;
+ }
+
+ /** Returns a {@link 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;
+ long mReceivedTimeEpochMillis;
+ boolean mIsRead;
+
+ /** Sets a {@link Person} representing the message sender */
+ public @NonNull Builder setSender(@NonNull Person sender) {
+ mSender = sender;
+ return this;
+ }
+
+ /** Sets a {@link CarText} representing the message body */
+ public @NonNull Builder setBody(@NonNull CarText body) {
+ mBody = body;
+ 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 {@link 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() {
+ return new CarMessage(this);
+ }
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
new file mode 100644
index 0000000..42eeeba
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
@@ -0,0 +1,160 @@
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+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.RequiresCarApi;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.CarText;
+import androidx.car.app.model.Item;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Represents a conversation */
+@ExperimentalCarApi
+@CarProtocol
+@RequiresCarApi(6)
+public class ConversationItem implements Item {
+ @NonNull private final String mId;
+ @NonNull private final CarText mTitle;
+ @Nullable
+ private final CarIcon mIcon;
+ private final boolean mIsGroupConversation;
+ @NonNull private final List<CarMessage> mMessages;
+
+ ConversationItem(@NonNull Builder builder) {
+ this.mId = requireNonNull(builder.mId);
+ this.mTitle = requireNonNull(builder.mTitle);
+ this.mIcon = builder.mIcon;
+ this.mIsGroupConversation = builder.mIsGroupConversation;
+ this.mMessages = requireNonNull(builder.mMessages);
+ }
+
+ /** Default constructor for serialization. */
+ private ConversationItem() {
+ mId = "";
+ mTitle = new CarText.Builder("").build();
+ mIcon = null;
+ mIsGroupConversation = false;
+ mMessages = new ArrayList<>();
+ }
+
+ /**
+ * Returns a unique identifier for the conversation
+ *
+ * @see Builder#setId
+ */
+ public @NonNull String getId() {
+ return mId;
+ }
+
+ /** Returns the title of the conversation */
+ public @NonNull CarText getTitle() {
+ return mTitle;
+ }
+
+ /** Returns a {@link CarIcon} for the conversation, or {@code null} if not set */
+ public @Nullable CarIcon getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Returns whether this conversation involves 3+ participants (a "group" conversation)
+ *
+ * @see Builder#setGroupConversation(boolean)
+ */
+ public boolean isGroupConversation() {
+ return mIsGroupConversation;
+ }
+
+ /** Returns a list of messages for this {@link ConversationItem} */
+ public @NonNull List<CarMessage> getMessages() {
+ return mMessages;
+ }
+
+ /** A builder for {@link ConversationItem} */
+ public static final class Builder {
+ @Nullable
+ String mId;
+ @Nullable
+ CarText mTitle;
+ @Nullable
+ CarIcon mIcon;
+ boolean mIsGroupConversation;
+ @Nullable
+ List<CarMessage> mMessages;
+
+ /**
+ * Specifies a unique identifier for the conversation
+ *
+ * <p> IDs may be used for a variety of purposes, including...
+ * <ul>
+ * <li> Distinguishing new {@link ConversationItem}s from updated
+ * {@link ConversationItem}s in the UI, when data is refreshed
+ * <li> Identifying {@link ConversationItem}s in "mark as read" / "reply" callbacks
+ * </ul>
+ */
+ public @NonNull Builder setId(@NonNull String id) {
+ mId = id;
+ return this;
+ }
+
+ /** Sets the title of the conversation */
+ public @NonNull Builder setTitle(@NonNull CarText title) {
+ mTitle = title;
+ return this;
+ }
+
+ /** Sets a {@link CarIcon} for the conversation */
+ public @NonNull Builder setIcon(@NonNull CarIcon icon) {
+ mIcon = icon;
+ return this;
+ }
+
+ /**
+ * Specifies whether this conversation involves 3+ participants (a "group" conversation)
+ *
+ * <p> If unspecified, conversations are assumed to have exactly two participants (a "1:1"
+ * conversation)
+ *
+ * <p> UX presentation may differ slightly between group and 1:1 conversations. As a
+ * historical example, message readout may include sender names for group conversations, but
+ * omit them for 1:1 conversations.
+ */
+ public @NonNull Builder setGroupConversation(boolean isGroupConversation) {
+ mIsGroupConversation = isGroupConversation;
+ return this;
+ }
+
+ /** Specifies a list of messages for the conversation */
+ public @NonNull Builder setMessages(@NonNull List<CarMessage> messages) {
+ mMessages = messages;
+ return this;
+ }
+
+ /** Returns a new {@link ConversationItem} instance defined by this builder */
+ public @NonNull ConversationItem build() {
+ return new ConversationItem(this);
+ }
+ }
+}