[go: nahoru, domu]

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);
+        }
+    }
+}