Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 82fa8d94 authored by Tony Mak's avatar Tony Mak
Browse files

Pass reference time / locales of messages to the model

1. It is required to set Person object when constructing a Message object
   now. As it is very important to know whether the message is from
   local user or remote user. Introduced PERSON_USER_REMOTE if
   the caller just want a simple way to specify a remote user.

2. Use detectLanguages to detect the locale of the messages
   If the model finds the detected language is not something
   it supports, model may suppress smart reply.

3. Pass the reference time to the model. So model can resolve
   the absolute time from a relative date string like "tomorrow 6pm".

BUG: 120809869

Test: atest ActionsSuggestionsHelperTest.java
Test: atest ConversationActionsTest.java

Change-Id: Ie079848e9b3d9bb8800f7f95d73e289e831968f8
parent af7deaba
Loading
Loading
Loading
Loading
+4 −4
Original line number Original line Diff line number Diff line
@@ -52488,19 +52488,19 @@ package android.view.textclassifier {
    method public int describeContents();
    method public int describeContents();
    method public android.app.Person getAuthor();
    method public android.app.Person getAuthor();
    method public android.os.Bundle getExtras();
    method public android.os.Bundle getExtras();
    method public java.time.ZonedDateTime getReferenceTime();
    method public java.lang.CharSequence getText();
    method public java.lang.CharSequence getText();
    method public java.time.ZonedDateTime getTime();
    method public void writeToParcel(android.os.Parcel, int);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Message> CREATOR;
    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Message> CREATOR;
    field public static final android.app.Person PERSON_USER_LOCAL;
    field public static final android.app.Person PERSON_USER_LOCAL;
    field public static final android.app.Person PERSON_USER_REMOTE;
  }
  }
  public static final class ConversationActions.Message.Builder {
  public static final class ConversationActions.Message.Builder {
    ctor public ConversationActions.Message.Builder();
    ctor public ConversationActions.Message.Builder(android.app.Person);
    method public android.view.textclassifier.ConversationActions.Message build();
    method public android.view.textclassifier.ConversationActions.Message build();
    method public android.view.textclassifier.ConversationActions.Message.Builder setAuthor(android.app.Person);
    method public android.view.textclassifier.ConversationActions.Message.Builder setComposeTime(java.time.ZonedDateTime);
    method public android.view.textclassifier.ConversationActions.Message.Builder setExtras(android.os.Bundle);
    method public android.view.textclassifier.ConversationActions.Message.Builder setExtras(android.os.Bundle);
    method public android.view.textclassifier.ConversationActions.Message.Builder setReferenceTime(java.time.ZonedDateTime);
    method public android.view.textclassifier.ConversationActions.Message.Builder setText(java.lang.CharSequence);
    method public android.view.textclassifier.ConversationActions.Message.Builder setText(java.lang.CharSequence);
  }
  }
+9 −22
Original line number Original line Diff line number Diff line
@@ -16,7 +16,6 @@


package android.view.textclassifier;
package android.view.textclassifier;


import android.annotation.NonNull;
import android.app.Person;
import android.app.Person;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArrayMap;
@@ -30,6 +29,7 @@ import java.util.ArrayList;
import java.util.Deque;
import java.util.Deque;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Collectors;


/**
/**
@@ -57,9 +57,9 @@ public final class ActionsSuggestionsHelper {
     * </ul>
     * </ul>
     * User A will be encoded as 2, user B will be encoded as 1 and local user will be encoded as 0.
     * User A will be encoded as 2, user B will be encoded as 1 and local user will be encoded as 0.
     */
     */
    @NonNull
    public static ActionsSuggestionsModel.ConversationMessage[] toNativeMessages(
    public static ActionsSuggestionsModel.ConversationMessage[] toNativeMessages(
            @NonNull List<ConversationActions.Message> messages) {
            List<ConversationActions.Message> messages,
            Function<CharSequence, String> languageDetector) {
        List<ConversationActions.Message> messagesWithText =
        List<ConversationActions.Message> messagesWithText =
                messages.stream()
                messages.stream()
                        .filter(message -> !TextUtils.isEmpty(message.getText()))
                        .filter(message -> !TextUtils.isEmpty(message.getText()))
@@ -67,31 +67,18 @@ public final class ActionsSuggestionsHelper {
        if (messagesWithText.isEmpty()) {
        if (messagesWithText.isEmpty()) {
            return new ActionsSuggestionsModel.ConversationMessage[0];
            return new ActionsSuggestionsModel.ConversationMessage[0];
        }
        }
        int size = messagesWithText.size();
        // If the last message (the most important one) does not have the Person object, we will
        // just use the last message and consider this message is sent from a remote user.
        ConversationActions.Message lastMessage = messages.get(size - 1);
        boolean useLastMessageOnly = lastMessage.getAuthor() == null;
        if (useLastMessageOnly) {
            return new ActionsSuggestionsModel.ConversationMessage[]{
                    new ActionsSuggestionsModel.ConversationMessage(
                            FIRST_NON_LOCAL_USER,
                            lastMessage.getText().toString(),
                            0,
                            null)};
        }

        // Encode the messages in the reverse order, stop whenever the Person object is missing.
        Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
        Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
        PersonEncoder personEncoder = new PersonEncoder();
        PersonEncoder personEncoder = new PersonEncoder();
        int size = messagesWithText.size();
        for (int i = size - 1; i >= 0; i--) {
        for (int i = size - 1; i >= 0; i--) {
            ConversationActions.Message message = messagesWithText.get(i);
            ConversationActions.Message message = messagesWithText.get(i);
            if (message.getAuthor() == null) {
            long referenceTime = message.getReferenceTime() == null
                break;
                    ? 0
            }
                    : message.getReferenceTime().toInstant().toEpochMilli();
            nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
            nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
                    personEncoder.encode(message.getAuthor()),
                    personEncoder.encode(message.getAuthor()),
                    message.getText().toString(), 0, null));
                    message.getText().toString(), referenceTime,
                    languageDetector.apply(message.getText())));
        }
        }
        return nativeMessages.toArray(
        return nativeMessages.toArray(
                new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
                new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
+46 −25
Original line number Original line Diff line number Diff line
@@ -349,17 +349,31 @@ public final class ConversationActions implements Parcelable {
        /**
        /**
         * Represents the local user.
         * Represents the local user.
         *
         *
         * @see Builder#setAuthor(Person)
         * @see Builder#Builder(Person)
         */
         */
        public static final Person PERSON_USER_LOCAL =
        public static final Person PERSON_USER_LOCAL =
                new Person.Builder()
                new Person.Builder()
                        .setKey("text-classifier-conversation-actions-local-user")
                        .setKey("text-classifier-conversation-actions-local-user")
                        .build();
                        .build();


        /**
         * Represents the remote user.
         * <p>
         * If possible, you are suggested to create a {@link Person} object that can identify
         * the remote user better, so that the underlying model could differentiate between
         * different remote users.
         *
         * @see Builder#Builder(Person)
         */
        public static final Person PERSON_USER_REMOTE =
                new Person.Builder()
                        .setKey("text-classifier-conversation-actions-remote-user")
                        .build();

        @Nullable
        @Nullable
        private final Person mAuthor;
        private final Person mAuthor;
        @Nullable
        @Nullable
        private final ZonedDateTime mComposeTime;
        private final ZonedDateTime mReferenceTime;
        @Nullable
        @Nullable
        private final CharSequence mText;
        private final CharSequence mText;
        @NonNull
        @NonNull
@@ -367,18 +381,18 @@ public final class ConversationActions implements Parcelable {


        private Message(
        private Message(
                @Nullable Person author,
                @Nullable Person author,
                @Nullable ZonedDateTime composeTime,
                @Nullable ZonedDateTime referenceTime,
                @Nullable CharSequence text,
                @Nullable CharSequence text,
                @NonNull Bundle bundle) {
                @NonNull Bundle bundle) {
            mAuthor = author;
            mAuthor = author;
            mComposeTime = composeTime;
            mReferenceTime = referenceTime;
            mText = text;
            mText = text;
            mExtras = Preconditions.checkNotNull(bundle);
            mExtras = Preconditions.checkNotNull(bundle);
        }
        }


        private Message(Parcel in) {
        private Message(Parcel in) {
            mAuthor = in.readParcelable(null);
            mAuthor = in.readParcelable(null);
            mComposeTime =
            mReferenceTime =
                    in.readInt() == 0
                    in.readInt() == 0
                            ? null
                            ? null
                            : ZonedDateTime.parse(
                            : ZonedDateTime.parse(
@@ -390,9 +404,9 @@ public final class ConversationActions implements Parcelable {
        @Override
        @Override
        public void writeToParcel(Parcel parcel, int flags) {
        public void writeToParcel(Parcel parcel, int flags) {
            parcel.writeParcelable(mAuthor, flags);
            parcel.writeParcelable(mAuthor, flags);
            parcel.writeInt(mComposeTime != null ? 1 : 0);
            parcel.writeInt(mReferenceTime != null ? 1 : 0);
            if (mComposeTime != null) {
            if (mReferenceTime != null) {
                parcel.writeString(mComposeTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
                parcel.writeString(mReferenceTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
            }
            }
            parcel.writeCharSequence(mText);
            parcel.writeCharSequence(mText);
            parcel.writeBundle(mExtras);
            parcel.writeBundle(mExtras);
@@ -417,15 +431,18 @@ public final class ConversationActions implements Parcelable {
                };
                };


        /** Returns the person that composed the message. */
        /** Returns the person that composed the message. */
        @Nullable
        @NonNull
        public Person getAuthor() {
        public Person getAuthor() {
            return mAuthor;
            return mAuthor;
        }
        }


        /** Returns the compose time of the message. */
        /**
         * Returns the reference time of the message, for example it could be the compose or send
         * time of this message.
         */
        @Nullable
        @Nullable
        public ZonedDateTime getTime() {
        public ZonedDateTime getReferenceTime() {
            return mComposeTime;
            return mReferenceTime;
        }
        }


        /** Returns the text of the message. */
        /** Returns the text of the message. */
@@ -451,34 +468,38 @@ public final class ConversationActions implements Parcelable {
            @Nullable
            @Nullable
            private Person mAuthor;
            private Person mAuthor;
            @Nullable
            @Nullable
            private ZonedDateTime mComposeTime;
            private ZonedDateTime mReferenceTime;
            @Nullable
            @Nullable
            private CharSequence mText;
            private CharSequence mText;
            @Nullable
            @Nullable
            private Bundle mExtras;
            private Bundle mExtras;


            /**
            /**
             * Sets the person who composed this message.
             * Constructs a builder.
             * <p>
             *
             * Use {@link #PERSON_USER_LOCAL} to represent the local user.
             * @param author the person that composed the message, use {@link #PERSON_USER_LOCAL}
             *               to represent the local user. If it is not possible to identify the
             *               remote user that the local user is conversing with, use
             *               {@link #PERSON_USER_REMOTE} to represent a remote user.
             */
             */
            @NonNull
            public Builder(@NonNull Person author) {
            public Builder setAuthor(@Nullable Person author) {
                mAuthor = Preconditions.checkNotNull(author);
                mAuthor = author;
                return this;
            }
            }


            /** Sets the text of this message */
            /** Sets the text of this message. */
            @NonNull
            @NonNull
            public Builder setText(@Nullable CharSequence text) {
            public Builder setText(@Nullable CharSequence text) {
                mText = text;
                mText = text;
                return this;
                return this;
            }
            }


            /** Sets the compose time of this message */
            /**
             * Sets the reference time of this message, for example it could be the compose or send
             * time of this message.
             */
            @NonNull
            @NonNull
            public Builder setComposeTime(@Nullable ZonedDateTime composeTime) {
            public Builder setReferenceTime(@Nullable ZonedDateTime referenceTime) {
                mComposeTime = composeTime;
                mReferenceTime = referenceTime;
                return this;
                return this;
            }
            }


@@ -494,7 +515,7 @@ public final class ConversationActions implements Parcelable {
            public Message build() {
            public Message build() {
                return new Message(
                return new Message(
                        mAuthor,
                        mAuthor,
                        mComposeTime,
                        mReferenceTime,
                        mText == null ? null : new SpannedString(mText),
                        mText == null ? null : new SpannedString(mText),
                        mExtras == null ? new Bundle() : mExtras.deepCopy());
                        mExtras == null ? new Bundle() : mExtras.deepCopy());
            }
            }
+22 −1
Original line number Original line Diff line number Diff line
@@ -374,7 +374,8 @@ public final class TextClassifierImpl implements TextClassifier {
                return mFallback.suggestConversationActions(request);
                return mFallback.suggestConversationActions(request);
            }
            }
            ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
            ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
                    ActionsSuggestionsHelper.toNativeMessages(request.getConversation());
                    ActionsSuggestionsHelper.toNativeMessages(request.getConversation(),
                            this::detectLanguageTagsFromText);
            if (nativeMessages.length == 0) {
            if (nativeMessages.length == 0) {
                return mFallback.suggestConversationActions(request);
                return mFallback.suggestConversationActions(request);
            }
            }
@@ -407,6 +408,26 @@ public final class TextClassifierImpl implements TextClassifier {
        return mFallback.suggestConversationActions(request);
        return mFallback.suggestConversationActions(request);
    }
    }


    @Nullable
    private String detectLanguageTagsFromText(CharSequence text) {
        TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
        TextLanguage textLanguage = detectLanguage(request);
        int localeHypothesisCount = textLanguage.getLocaleHypothesisCount();
        List<String> languageTags = new ArrayList<>();
        // TODO: Reconsider this and probably make the score threshold configurable.
        for (int i = 0; i < localeHypothesisCount; i++) {
            ULocale locale = textLanguage.getLocale(i);
            if (textLanguage.getConfidenceScore(locale) < 0.5) {
                break;
            }
            languageTags.add(locale.toLanguageTag());
        }
        if (languageTags.isEmpty()) {
            return LocaleList.getDefault().toLanguageTags();
        }
        return String.join(",", languageTags);
    }

    private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) {
    private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) {
        List<String> defaultActionTypes =
        List<String> defaultActionTypes =
                request.getHints().contains(ConversationActions.HINT_FOR_NOTIFICATION)
                request.getHints().contains(ConversationActions.HINT_FOR_NOTIFICATION)
+50 −62
Original line number Original line Diff line number Diff line
@@ -16,6 +16,9 @@


package android.view.textclassifier;
package android.view.textclassifier;


import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_LOCAL;
import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_REMOTE;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertThat;


import android.app.Person;
import android.app.Person;
@@ -27,16 +30,26 @@ import com.google.android.textclassifier.ActionsSuggestionsModel;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;


import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Collections;
import java.util.Collections;
import java.util.Locale;
import java.util.function.Function;


@SmallTest
@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWith(AndroidJUnit4.class)
public class ActionsSuggestionsHelperTest {
public class ActionsSuggestionsHelperTest {
    private static final String LOCALE_TAG = Locale.US.toLanguageTag();
    private static final Function<CharSequence, String> LANGUAGE_DETECTOR =
            charSequence -> LOCALE_TAG;

    @Test
    @Test
    public void testToNativeMessages_emptyInput() {
    public void testToNativeMessages_emptyInput() {
        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
                ActionsSuggestionsHelper.toNativeMessages(Collections.emptyList());
                ActionsSuggestionsHelper.toNativeMessages(
                        Collections.emptyList(), LANGUAGE_DETECTOR);


        assertThat(conversationMessages).isEmpty();
        assertThat(conversationMessages).isEmpty();
    }
    }
@@ -44,114 +57,89 @@ public class ActionsSuggestionsHelperTest {
    @Test
    @Test
    public void testToNativeMessages_noTextMessages() {
    public void testToNativeMessages_noTextMessages() {
        ConversationActions.Message messageWithoutText =
        ConversationActions.Message messageWithoutText =
                new ConversationActions.Message.Builder().build();
                new ConversationActions.Message.Builder(PERSON_USER_REMOTE).build();


        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
                ActionsSuggestionsHelper.toNativeMessages(
                ActionsSuggestionsHelper.toNativeMessages(
                        Collections.singletonList(messageWithoutText));
                        Collections.singletonList(messageWithoutText), LANGUAGE_DETECTOR);


        assertThat(conversationMessages).isEmpty();
        assertThat(conversationMessages).isEmpty();
    }
    }


    @Test
    @Test
    public void testToNativeMessages_missingPersonInFirstMessage() {
    public void testToNativeMessages_userIdEncoding() {
        ConversationActions.Message firstMessage =
        Person userA = new Person.Builder().setName("userA").build();
                new ConversationActions.Message.Builder()
        Person userB = new Person.Builder().setName("userB").build();
                        .setText("first")
                        .build();
        ConversationActions.Message secondMessage =
                new ConversationActions.Message.Builder()
                        .setText("second")
                        .setAuthor(new Person.Builder().build())
                        .build();
        ConversationActions.Message thirdMessage =
                new ConversationActions.Message.Builder()
                        .setText("third")
                        .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
                        .build();

        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
                ActionsSuggestionsHelper.toNativeMessages(
                        Arrays.asList(firstMessage, secondMessage, thirdMessage));

        assertThat(conversationMessages).hasLength(2);
        assertNativeMessage(conversationMessages[0], secondMessage.getText(), 1);
        assertNativeMessage(conversationMessages[1], thirdMessage.getText(), 0);
    }


    @Test
    public void testToNativeMessages_missingPersonInMiddleOfConversation() {
        ConversationActions.Message firstMessage =
        ConversationActions.Message firstMessage =
                new ConversationActions.Message.Builder()
                new ConversationActions.Message.Builder(userB)
                        .setText("first")
                        .setText("first")
                        .setAuthor(new Person.Builder().setName("first").build())
                        .build();
                        .build();
        ConversationActions.Message secondMessage =
        ConversationActions.Message secondMessage =
                new ConversationActions.Message.Builder()
                new ConversationActions.Message.Builder(userA)
                        .setText("second")
                        .setText("second")
                        .build();
                        .build();
        ConversationActions.Message thirdMessage =
        ConversationActions.Message thirdMessage =
                new ConversationActions.Message.Builder()
                new ConversationActions.Message.Builder(PERSON_USER_LOCAL)
                        .setText("third")
                        .setText("third")
                        .setAuthor(new Person.Builder().setName("third").build())
                        .build();
                        .build();
        ConversationActions.Message fourthMessage =
        ConversationActions.Message fourthMessage =
                new ConversationActions.Message.Builder()
                new ConversationActions.Message.Builder(userA)
                        .setText("fourth")
                        .setText("fourth")
                        .setAuthor(new Person.Builder().setName("fourth").build())
                        .build();
                        .build();


        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
                ActionsSuggestionsHelper.toNativeMessages(
                ActionsSuggestionsHelper.toNativeMessages(
                        Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
                        Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage),
                        LANGUAGE_DETECTOR);


        assertThat(conversationMessages).hasLength(2);
        assertThat(conversationMessages).hasLength(4);
        assertNativeMessage(conversationMessages[0], thirdMessage.getText(), 2);
        assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2, 0);
        assertNativeMessage(conversationMessages[1], fourthMessage.getText(), 1);
        assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
        assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0, 0);
        assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1, 0);
    }
    }


    @Test
    @Test
    public void testToNativeMessages_userIdEncoding() {
    public void testToNativeMessages_referenceTime() {
        Person userA = new Person.Builder().setName("userA").build();
        Person userB = new Person.Builder().setName("userB").build();

        ConversationActions.Message firstMessage =
        ConversationActions.Message firstMessage =
                new ConversationActions.Message.Builder()
                new ConversationActions.Message.Builder(PERSON_USER_REMOTE)
                        .setText("first")
                        .setText("first")
                        .setAuthor(userB)
                        .setReferenceTime(createZonedDateTimeFromMsUtc(1000))
                        .build();
                        .build();
        ConversationActions.Message secondMessage =
        ConversationActions.Message secondMessage =
                new ConversationActions.Message.Builder()
                new ConversationActions.Message.Builder(PERSON_USER_REMOTE)
                        .setText("second")
                        .setText("second")
                        .setAuthor(userA)
                        .build();
                        .build();
        ConversationActions.Message thirdMessage =
        ConversationActions.Message thirdMessage =
                new ConversationActions.Message.Builder()
                new ConversationActions.Message.Builder(PERSON_USER_REMOTE)
                        .setText("third")
                        .setText("third")
                        .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
                        .setReferenceTime(createZonedDateTimeFromMsUtc(2000))
                        .build();
        ConversationActions.Message fourthMessage =
                new ConversationActions.Message.Builder()
                        .setText("fourth")
                        .setAuthor(userA)
                        .build();
                        .build();


        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
                ActionsSuggestionsHelper.toNativeMessages(
                ActionsSuggestionsHelper.toNativeMessages(
                        Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
                        Arrays.asList(firstMessage, secondMessage, thirdMessage),
                        LANGUAGE_DETECTOR);


        assertThat(conversationMessages).hasLength(4);
        assertThat(conversationMessages).hasLength(3);
        assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2);
        assertNativeMessage(conversationMessages[0], firstMessage.getText(), 1, 1000);
        assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1);
        assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
        assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0);
        assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 1, 2000);
        assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1);
    }

    private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
        return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneId.of("UTC"));
    }
    }


    private static void assertNativeMessage(
    private static void assertNativeMessage(
            ActionsSuggestionsModel.ConversationMessage nativeMessage,
            ActionsSuggestionsModel.ConversationMessage nativeMessage,
            CharSequence text,
            CharSequence text,
            int userId) {
            int userId,
            long referenceTimeInMsUtc) {
        assertThat(nativeMessage.getText()).isEqualTo(text.toString());
        assertThat(nativeMessage.getText()).isEqualTo(text.toString());
        assertThat(nativeMessage.getUserId()).isEqualTo(userId);
        assertThat(nativeMessage.getUserId()).isEqualTo(userId);
        assertThat(nativeMessage.getLocales()).isEqualTo(LOCALE_TAG);
        assertThat(nativeMessage.getReferenceTimeMsUtc()).isEqualTo(referenceTimeInMsUtc);
    }
    }
}
}
Loading