1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.messenger.common; 18 19 import static com.android.car.apps.common.util.SafeLog.logw; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.bluetooth.BluetoothDevice; 24 import android.content.Intent; 25 import android.graphics.drawable.Icon; 26 import android.os.Build; 27 import android.util.Log; 28 29 import com.android.car.messenger.NotificationMsgProto.NotificationMsg; 30 import com.android.car.messenger.NotificationMsgProto.NotificationMsg.ConversationNotification; 31 import com.android.car.messenger.NotificationMsgProto.NotificationMsg.MessagingStyle; 32 import com.android.car.messenger.NotificationMsgProto.NotificationMsg.PhoneToCarMessage; 33 34 import java.util.LinkedList; 35 import java.util.List; 36 37 /** 38 * Represents a conversation notification's metadata that is shared between the conversation's 39 * messages. Note, each {@link ConversationKey} should map to exactly one 40 * ConversationNotificationInfo object. 41 **/ 42 public class ConversationNotificationInfo { 43 private static final String TAG = "CMC.ConvoNotifInfo"; 44 private static int sNextNotificationId = 0; 45 final int mNotificationId = sNextNotificationId++; 46 47 private final String mDeviceName; 48 private final String mDeviceId; 49 // This is always the sender name for SMS Messages from Bluetooth MAP. 50 private String mConvoTitle; 51 private final boolean mIsGroupConvo; 52 53 /** Only used for {@link NotificationMsg} conversations. **/ 54 @Nullable 55 private final String mNotificationKey; 56 @Nullable 57 private final String mAppDisplayName; 58 private final String mAppPackageName; 59 @Nullable 60 private final String mUserDisplayName; 61 @Nullable 62 private final Icon mAppIcon; 63 /** Uris of all members in a MMS Group Conversation. **/ 64 @Nullable 65 private final List<String> mCcRecipientsUris; 66 67 public final LinkedList<MessageKey> mMessageKeys = new LinkedList<>(); 68 69 /** 70 * Creates a ConversationNotificationInfo for a {@link NotificationMsg}. Returns {@code null} if 71 * the {@link ConversationNotification} is missing required fields. 72 **/ 73 @Nullable createConversationNotificationInfo( @onNull String deviceName, @NonNull String deviceId, @NonNull ConversationNotification conversation, @NonNull String notificationKey)74 public static ConversationNotificationInfo createConversationNotificationInfo( 75 @NonNull String deviceName, @NonNull String deviceId, 76 @NonNull ConversationNotification conversation, @NonNull String notificationKey) { 77 MessagingStyle messagingStyle = conversation.getMessagingStyle(); 78 79 if (!Utils.isValidConversationNotification(conversation, /* isShallowCheck= */ true)) { 80 if (Log.isLoggable(TAG, Log.DEBUG) || Build.IS_DEBUGGABLE) { 81 throw new IllegalArgumentException( 82 "ConversationNotificationInfo is missing required fields"); 83 } else { 84 logw(TAG, "ConversationNotificationInfo is missing required fields"); 85 return null; 86 } 87 } 88 89 Icon appIcon = null; 90 if (conversation.getAppIcon() != null) { 91 byte[] iconBytes = conversation.getAppIcon().toByteArray(); 92 appIcon = Icon.createWithData(iconBytes, 0, iconBytes.length); 93 } 94 95 return new ConversationNotificationInfo(deviceName, deviceId, 96 messagingStyle.getConvoTitle(), 97 messagingStyle.getIsGroupConvo(), notificationKey, 98 conversation.getMessagingAppDisplayName(), 99 conversation.getMessagingAppPackageName(), 100 messagingStyle.getUserDisplayName(), 101 appIcon, 102 /* ccUris= */null); 103 104 } 105 /** Creates a ConversationNotificationInfo for a BluetoothMapClient intent. **/ createConversationNotificationInfo(Intent intent, String conversationTitle, String appPackageName, @Nullable Icon appIcon)106 public static ConversationNotificationInfo createConversationNotificationInfo(Intent intent, 107 String conversationTitle, String appPackageName, @Nullable Icon appIcon) { 108 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 109 110 return new ConversationNotificationInfo(device.getName(), device.getAddress(), 111 conversationTitle, Utils.isGroupConversation(intent), /* notificationKey */ null, 112 /* appDisplayName */ null, appPackageName, /* userDisplayName */ null, 113 appIcon, 114 Utils.getInclusiveRecipientsUrisList(intent)); 115 } 116 ConversationNotificationInfo(@ullable String deviceName, String deviceId, String convoTitle, boolean isGroupConvo, @Nullable String notificationKey, @Nullable String appDisplayName, String appPackageName, @Nullable String userDisplayName, @Nullable Icon appIcon, @Nullable List<String> ccUris)117 private ConversationNotificationInfo(@Nullable String deviceName, String deviceId, 118 String convoTitle, boolean isGroupConvo, @Nullable String notificationKey, 119 @Nullable String appDisplayName, String appPackageName, 120 @Nullable String userDisplayName, @Nullable Icon appIcon, 121 @Nullable List<String> ccUris) { 122 boolean missingDeviceId = (deviceId == null); 123 boolean missingTitle = (convoTitle == null); 124 if (missingDeviceId || missingTitle) { 125 StringBuilder builder = new StringBuilder("Missing required fields:"); 126 if (missingDeviceId) { 127 builder.append(" deviceId"); 128 } 129 if (missingTitle) { 130 builder.append(" convoTitle"); 131 } 132 throw new IllegalArgumentException(builder.toString()); 133 } 134 this.mDeviceName = deviceName; 135 this.mDeviceId = deviceId; 136 this.mConvoTitle = convoTitle; 137 this.mIsGroupConvo = isGroupConvo; 138 this.mNotificationKey = notificationKey; 139 this.mAppDisplayName = appDisplayName; 140 this.mAppPackageName = appPackageName; 141 this.mUserDisplayName = userDisplayName; 142 this.mAppIcon = appIcon; 143 this.mCcRecipientsUris = ccUris; 144 } 145 146 /** Returns the id that should be used for this object's {@link android.app.Notification} **/ getNotificationId()147 public int getNotificationId() { 148 return mNotificationId; 149 } 150 151 /** Returns the friendly name of the device that received the notification. **/ getDeviceName()152 public String getDeviceName() { 153 return mDeviceName; 154 } 155 156 /** Returns the address of the device that received the notification. **/ getDeviceId()157 public String getDeviceId() { 158 return mDeviceId; 159 } 160 161 /** 162 * Returns the conversation title of this notification. If this notification came from MAP 163 * profile, the title will be the Sender's name. 164 */ getConvoTitle()165 public String getConvoTitle() { 166 return mConvoTitle; 167 } 168 169 /** Update the conversation title. **/ setConvoTitle(String newTitle)170 public void setConvoTitle(String newTitle) { 171 mConvoTitle = newTitle; 172 } 173 174 /** Returns {@code true} if this message is in a group conversation **/ isGroupConvo()175 public boolean isGroupConvo() { 176 return mIsGroupConvo; 177 } 178 179 /** 180 * Returns the key if this conversation is based on a {@link ConversationNotification}. Refer to 181 * {@link PhoneToCarMessage#getNotificationKey()} for more info. 182 */ 183 @Nullable getNotificationKey()184 public String getNotificationKey() { 185 return mNotificationKey; 186 } 187 188 /** 189 * Returns the display name of the application that posted this notification if this object is 190 * based on a {@link ConversationNotification}. 191 **/ 192 @Nullable getAppDisplayName()193 public String getAppDisplayName() { 194 return mAppDisplayName; 195 } 196 197 /** 198 * Returns the package name of the application that posted this notification. 199 **/ getAppPackageName()200 public String getAppPackageName() { 201 return mAppPackageName; 202 } 203 204 /** 205 * Returns the User Display Name if this object is based on a @link ConversationNotification}. 206 * This is needed for {@link android.app.Notification.MessagingStyle}. 207 */ 208 @Nullable getUserDisplayName()209 public String getUserDisplayName() { 210 return mUserDisplayName; 211 } 212 213 214 /** Returns the app's icon of the application that posted this notification. **/ 215 @Nullable getAppIcon()216 public Icon getAppIcon() { 217 return mAppIcon; 218 } 219 getLastMessageKey()220 public MessageKey getLastMessageKey() { 221 return mMessageKeys.getLast(); 222 } 223 224 /** 225 * Returns the sorted URIs of all the participants of a MMS/SMS/RCS conversation. Returns 226 * {@code null} if this is based on a {@link NotificationMsg} conversation. 227 */ 228 @Nullable getCcRecipientsUris()229 public List<String> getCcRecipientsUris() { 230 return mCcRecipientsUris; 231 } 232 } 233