From c7df51807e792be10bd477a4930b4dec633dcad6 Mon Sep 17 00:00:00 2001 From: Peter Li Date: Tue, 9 Nov 2021 20:13:10 -0800 Subject: Fix receiving old text message in HUN Changes cursor queries to split into SMS and MMS to get around timestamp inconsistency. PiperOrigin-RevId: 408770794 Change-Id: I9ad9589a0df93d3d27744b7353849a119b5a160c Bug: 195684900 --- .../impl/datamodels/NewMessageLiveData.java | 2 +- .../datamodels/util/ConversationFetchUtil.java | 26 +++++- .../impl/datamodels/util/CursorUtils.java | 22 ++++- .../impl/datamodels/util/MessageUtils.java | 100 ++++++++++++--------- 4 files changed, 103 insertions(+), 47 deletions(-) diff --git a/src/com/android/car/messenger/impl/datamodels/NewMessageLiveData.java b/src/com/android/car/messenger/impl/datamodels/NewMessageLiveData.java index f725418..a5e0922 100644 --- a/src/com/android/car/messenger/impl/datamodels/NewMessageLiveData.java +++ b/src/com/android/car/messenger/impl/datamodels/NewMessageLiveData.java @@ -65,7 +65,7 @@ public class NewMessageLiveData extends ContentProviderLiveData { private final CarStateListener mCarStateListener = AppFactory.get().getCarStateListener(); NewMessageLiveData() { - super(Telephony.Sms.CONTENT_URI, Telephony.Mms.CONTENT_URI, Telephony.MmsSms.CONTENT_URI); + super(Telephony.MmsSms.CONTENT_URI); } @Override diff --git a/src/com/android/car/messenger/impl/datamodels/util/ConversationFetchUtil.java b/src/com/android/car/messenger/impl/datamodels/util/ConversationFetchUtil.java index 5cea752..b066417 100644 --- a/src/com/android/car/messenger/impl/datamodels/util/ConversationFetchUtil.java +++ b/src/com/android/car/messenger/impl/datamodels/util/ConversationFetchUtil.java @@ -55,17 +55,25 @@ public class ConversationFetchUtil { public static Conversation fetchConversation(@NonNull String conversationId) { L.d("Fetching latest data for Conversation " + conversationId); Conversation.Builder conversationBuilder = initConversationBuilder(conversationId); - Cursor messagesCursor = - CursorUtils.getMessagesCursor(conversationId, MESSAGE_LIMIT, /* offset= */ 0); + Cursor mmsCursor = getMmsCursor(conversationId); + Cursor smsCursor = getSmsCursor(conversationId); + + // message list sorted by date desc + List messages = + MessageUtils.getMessages(MESSAGE_LIMIT, mmsCursor, smsCursor); + // messages to read: first get unread messages - List messagesToRead = MessageUtils.getUnreadMessages(messagesCursor); + // List should truncate at the latest reply or read message since reading a recent message + // does not mark all previous messages read. + List messagesToRead = MessageUtils.getUnreadMessages(messages); + int unreadCount = messagesToRead.size(); Conversation.Message lastReply = null; // if no unread messages, get read messages if (messagesToRead.isEmpty()) { Pair, Conversation.Message> readMessagesAndReplyTimestamp = - MessageUtils.getReadMessagesAndReplyTimestamp(messagesCursor); + MessageUtils.getReadMessagesAndReplyTimestamp(messages); messagesToRead = readMessagesAndReplyTimestamp.first; lastReply = readMessagesAndReplyTimestamp.second; } @@ -152,4 +160,14 @@ public class ConversationFetchUtil { return sharedPreferences.getStringSet( MessageConstants.KEY_MUTED_CONVERSATIONS, new HashSet<>()); } + + private static Cursor getMmsCursor(@NonNull String conversationId) { + return CursorUtils.getMessagesCursor( + conversationId, MESSAGE_LIMIT, /* offset= */ 0, CursorUtils.ContentType.MMS); + } + + private static Cursor getSmsCursor(@NonNull String conversationId) { + return CursorUtils.getMessagesCursor( + conversationId, MESSAGE_LIMIT, /* offset= */ 0, CursorUtils.ContentType.SMS); + } } diff --git a/src/com/android/car/messenger/impl/datamodels/util/CursorUtils.java b/src/com/android/car/messenger/impl/datamodels/util/CursorUtils.java index 5ce2302..93b6a85 100644 --- a/src/com/android/car/messenger/impl/datamodels/util/CursorUtils.java +++ b/src/com/android/car/messenger/impl/datamodels/util/CursorUtils.java @@ -28,6 +28,8 @@ import static android.provider.Telephony.ThreadsColumns.DATE; import static android.provider.Telephony.ThreadsColumns.READ; import static android.provider.Telephony.ThreadsColumns.RECIPIENT_IDS; +import static com.android.car.messenger.impl.datamodels.util.MmsUtils.MMS_CONTENT_TYPE; + import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; @@ -59,6 +61,16 @@ public class CursorUtils { @NonNull public static final String DEFAULT_SORT_ORDER = Telephony.TextBasedSmsColumns.DATE + " DESC"; + private static final String MMS_QUERY = + CONTENT_TYPE + " = '" + MMS_CONTENT_TYPE + "' AND " + DATE + " > "; + private static final String SMS_QUERY = CONTENT_TYPE + " IS NULL AND " + DATE + " > "; + + /** This enum is used for describing the type of message being fetched by a cursor */ + public enum ContentType { + SMS, + MMS + } + /** * Get simplified thread cursor with metadata information on the thread, such as recipient ids */ @@ -82,13 +94,19 @@ public class CursorUtils { * @param offset The starting point in timestamp in millisecond to fetch for data */ @Nullable - public static Cursor getMessagesCursor(@NonNull String conversationId, int limit, long offset) { + public static Cursor getMessagesCursor(@NonNull String conversationId, int limit, long offset, + @NonNull ContentType contentType) { Context context = AppFactory.get().getContext(); ContentResolver contentResolver = context.getContentResolver(); + + String query = contentType == ContentType.MMS + ? MMS_QUERY + offset / 1000 + : SMS_QUERY + offset; + return contentResolver.query( getConversationUri(conversationId), CONTENT_CONVERSATION_PROJECTION, - DATE + " > " + offset, + query, /* selectionArgs= */ null, DEFAULT_SORT_ORDER + " LIMIT " + limit); } diff --git a/src/com/android/car/messenger/impl/datamodels/util/MessageUtils.java b/src/com/android/car/messenger/impl/datamodels/util/MessageUtils.java index 555d91c..0bb454b 100644 --- a/src/com/android/car/messenger/impl/datamodels/util/MessageUtils.java +++ b/src/com/android/car/messenger/impl/datamodels/util/MessageUtils.java @@ -20,6 +20,7 @@ import static com.android.car.messenger.common.Conversation.Message.MessageStatu import static com.android.car.messenger.common.Conversation.Message.MessageStatus.MESSAGE_STATUS_READ; import static com.android.car.messenger.common.Conversation.Message.MessageStatus.MESSAGE_STATUS_UNREAD; +import static java.lang.Math.min; import static java.util.Comparator.comparingLong; import android.content.Context; @@ -47,61 +48,80 @@ import java.util.function.Function; public final class MessageUtils { /** - * Gets all unread messages in cursor + * Returns all messages in the given cursors. * - * @param messagesCursor The messageCursor in descending order + * @param limit The maximum number of messages + * @param messageCursors The messageCursors of messages in descending order */ @NonNull - public static List getUnreadMessages(@Nullable Cursor messagesCursor) { - List unreadMessages = new ArrayList<>(); - MessageUtils.forEachDesc( - messagesCursor, - message -> { - if (message.getMessageStatus() == MessageStatus.MESSAGE_STATUS_UNREAD) { - unreadMessages.add(message); + public static List getMessages(int limit, @Nullable Cursor... messageCursors) { + List messages = new ArrayList<>(); + for (Cursor cursor : messageCursors) { + MessageUtils.forEachDesc( + cursor, + message -> { + messages.add(message); return true; - } - return false; - }); - unreadMessages.sort(comparingLong(Message::getTimestamp)); + }); + } + messages.sort(comparingLong(Message::getTimestamp).reversed()); + return messages.subList(0, min(limit, messages.size())); + } + + /** + * Returns unread messages from a conversation, in ascending order. + * + * @param messages The messages in descending order + */ + @NonNull + public static List getUnreadMessages(@NonNull List messages) { + int i = 0; + for (Conversation.Message message : messages) { + if (message.getMessageStatus() != MessageStatus.MESSAGE_STATUS_UNREAD) { + break; + } + i++; + } + List unreadMessages = messages.subList(0, i); + unreadMessages.sort(comparingLong(Conversation.Message::getTimestamp)); return unreadMessages; } + /** * Gets Read Messages and Last Reply * - * @param messagesCursor MessageCursor in descending order + * @param messages List of messages in descending order */ @NonNull public static Pair, Message> getReadMessagesAndReplyTimestamp( - @Nullable Cursor messagesCursor) { + @Nullable List messages) { List readMessages = new ArrayList<>(); AtomicReference replyMessage = new AtomicReference<>(); AtomicReference lastReply = new AtomicReference<>(0L); - MessageUtils.forEachDesc( - messagesCursor, - message -> { - // Desired impact: 4. Reply -> 3. Messages -> 2. Reply -> 1 Messages (stop - // parsing at 2.) - // lastReply references 4., messages references 3. - // Desired impact: 3. Messages -> 2. Reply -> 1. Messages (stop parsing at 2.) - // lastReply references 2., messages references 3. - int messageStatus = message.getMessageStatus(); - if (message.getMessageType() == MessageType.MESSAGE_TYPE_SENT) { - if (lastReply.get() < message.getTimestamp()) { - lastReply.set(message.getTimestamp()); - replyMessage.set(message); - } - return readMessages.isEmpty(); - } - - if (messageStatus == MessageStatus.MESSAGE_STATUS_READ - || messageStatus == MessageStatus.MESSAGE_STATUS_NONE) { - readMessages.add(message); - return true; - } - return false; - }); + + for (Message message : messages) { + // Desired impact: 4. Reply -> 3. Messages -> 2. Reply -> 1 Messages (stop + // parsing at 2.) + // lastReply references 4., messages references 3. + // Desired impact: 3. Messages -> 2. Reply -> 1. Messages (stop parsing at 2.) + // lastReply references 2., messages references 3. + int messageStatus = message.getMessageStatus(); + if (message.getMessageType() == MessageType.MESSAGE_TYPE_SENT) { + if (lastReply.get() < message.getTimestamp()) { + lastReply.set(message.getTimestamp()); + replyMessage.set(message); + } + if (!readMessages.isEmpty()) { + break; + } + } else if (messageStatus == MessageStatus.MESSAGE_STATUS_READ + || messageStatus == MessageStatus.MESSAGE_STATUS_NONE) { + readMessages.add(message); + } else { + break; + } + } readMessages.sort(comparingLong(Message::getTimestamp)); return new Pair<>(readMessages, replyMessage.get()); } @@ -113,7 +133,7 @@ public final class MessageUtils { * @param processor A consumer that takes in the {@link Message} and returns true for the method * to continue parsing the cursor or false to return. */ - public static void forEachDesc( + private static void forEachDesc( @Nullable Cursor messageCursor, @NonNull Function processor) { if (messageCursor == null || !messageCursor.moveToFirst()) { return; -- cgit v1.2.3