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