blob: b0664172c06867638610fb76fda6b56cc45f2818 [file] [log] [blame]
/*
* Copyright (C) 2021 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 com.android.car.messenger.impl.datamodels.util;
import static java.lang.Math.min;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Bitmap;
import androidx.core.graphics.drawable.IconCompat;
import android.text.TextUtils;
import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.core.app.Person;
import com.android.car.messenger.R;
import com.android.car.messenger.common.Conversation;
import com.android.car.messenger.core.interfaces.AppFactory;
import com.android.car.messenger.core.shared.MessageConstants;
import com.android.car.messenger.core.util.ConversationUtil;
import com.android.car.messenger.core.util.L;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
/** Utility class for retrieving and setting conversation items. */
public class ConversationFetchUtil {
private static final int MESSAGE_LIMIT = 10;
private static final String COMMA_DELIMITER = ", ";
private static final int MAX_TITLE_NAMES = 3;
private ConversationFetchUtil() {}
/** Fetches a conversation item based on a provided conversation id */
public static Conversation fetchConversation(@NonNull String conversationId) {
L.d("Fetching latest data for Conversation " + conversationId);
Conversation.Builder conversationBuilder = initConversationBuilder(conversationId);
Cursor mmsCursor = getMmsCursor(conversationId);
Cursor smsCursor = getSmsCursor(conversationId);
// message list sorted by date desc
List<Conversation.Message> messages =
MessageUtils.getMessages(MESSAGE_LIMIT, mmsCursor, smsCursor);
// messages to read: first get unread messages
// List should truncate at the latest reply or read message since reading a recent message
// does not mark all previous messages read.
List<Conversation.Message> messagesToRead = MessageUtils.getUnreadMessages(messages);
int unreadCount = messagesToRead.size();
Conversation.Message lastReply = null;
// if no unread messages, get read messages
if (messagesToRead.isEmpty()) {
Pair<List<Conversation.Message>, Conversation.Message> readMessagesAndReplyTimestamp =
MessageUtils.getReadMessagesAndReplyTimestamp(messages);
messagesToRead = readMessagesAndReplyTimestamp.first;
lastReply = readMessagesAndReplyTimestamp.second;
}
conversationBuilder.setMessages(messagesToRead).setUnreadCount(unreadCount);
ConversationUtil.setReplyAsAnExtra(conversationBuilder, /* extras= */ null, lastReply);
return conversationBuilder.build();
}
@NonNull
private static Conversation.Builder initConversationBuilder(@NonNull String conversationId) {
Context context = AppFactory.get().getContext();
String userName = ContactUtils.DRIVER_NAME;
Conversation.Builder builder =
new Conversation.Builder(
new Person.Builder().setName(userName).build(), conversationId);
List<Person> participants =
fetchParticipants(
conversationId,
(names, icons) -> {
builder.setConversationTitle(formatConversationTitle(names));
Bitmap bitmap = AvatarUtil.createGroupAvatar(context, icons);
if (bitmap != null) {
builder.setConversationIcon(IconCompat.createWithBitmap(bitmap));
}
});
builder.setParticipants(participants);
builder.setMuted(loadMutedList().contains(conversationId));
return builder;
}
private static String formatConversationTitle(List<CharSequence> names) {
Context context = AppFactory.get().getContext();
String title =
TextUtils.join(
COMMA_DELIMITER, names.subList(0, min(MAX_TITLE_NAMES, names.size())));
if (names.size() > MAX_TITLE_NAMES) {
title +=
context.getString(
R.string.participant_overflow_text, names.size() - MAX_TITLE_NAMES);
}
return title;
}
/**
* Fetches participants and allows caller to process names and icons before returning.
*
* <p>For context, a conversation often holds multiple messages, which holds multiple
* participant contacts, which each in turn could hold an avatar.
*
* <p>This leads to a very heavy conversation class and leads to problems down the road when
* sending this conversation as a bundle.
*
* <p>To mitigate this, {@link Person} classes do not hold an avatar. Instead, each contact
* avatar is channeled up to the caller during a fetch to make one avatar for the entire
* conversation.
*
* @param conversationId, id for conversation to fetch participants information
* @param processNamesAndIcons the method to process the names and icons of the participants
* @return list of participants as {@link Person}. For performance reasons, the objects do not
* contain an avatar, and a functional interface is needed in order to process the various
* participant icons nto one conversation icon.
*/
private static List<Person> fetchParticipants(
@NonNull String conversationId,
@NonNull BiConsumer<List<CharSequence>, List<Bitmap>> processNamesAndIcons) {
List<CharSequence> participantNames = new ArrayList<>();
List<Bitmap> participantIcons = new ArrayList<>();
List<Person> participants =
ContactUtils.getRecipients(
conversationId,
(name, bitmap) -> {
participantNames.add(name);
participantIcons.add(bitmap);
});
processNamesAndIcons.accept(participantNames, participantIcons);
return participants;
}
/** Returns a set of muted conversation items */
@NonNull
public static Set<String> loadMutedList() {
SharedPreferences sharedPreferences = AppFactory.get().getSharedPreferences();
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);
}
}