• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.notification.utils;
18 
19 import static android.app.PendingIntent.FLAG_IMMUTABLE;
20 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
21 
22 import static androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ;
23 import static androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_REPLY;
24 
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.when;
27 
28 import android.app.Notification;
29 import android.app.PendingIntent;
30 import android.app.Service;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.graphics.drawable.Icon;
34 import android.os.Bundle;
35 import android.os.IBinder;
36 
37 import androidx.annotation.Nullable;
38 import androidx.core.app.NotificationCompat;
39 import androidx.core.app.NotificationCompat.Action;
40 import androidx.core.app.NotificationCompat.MessagingStyle;
41 import androidx.core.app.NotificationCompat.MessagingStyle.Message;
42 import androidx.core.app.Person;
43 import androidx.core.app.RemoteInput;
44 import androidx.core.graphics.drawable.IconCompat;
45 
46 import java.time.Instant;
47 
48 /**
49  * Builds {@link Notification}s for testing.
50  */
51 public class MockMessageNotificationBuilder {
52     private static final String SENDER_NAME = "senderName";
53     private static final String USER_NAME = "userName";
54     private static final String TITLE_MARK_AS_READ = "Mark As Read";
55     private static final String TITLE_REPLY = "Reply";
56     private static final String CONTENT_TITLE_PREFIX = "2 new messages from ";
57     private static final String CONTENT_SUBJECT = "subject";
58     private static final String OLD_MESSAGE = "old message";
59     private static final String FIRST_MESSAGE = "first message";
60     private static final String SECOND_MESSAGE = "second message";
61 
62     private final Context mContext;
63     private final Icon mActionIcon;
64     private final Person mSender;
65     private final Person mUser;
66     private final String mChannelId;
67     private final int mSmallIconId;
68     @Nullable
69     private Message mPostSpecificMessage = null;
70     @Nullable
71     private Bundle mExtras = null;
72     private boolean mHasMessagingStyle = false;
73     private boolean mIsOldMessage = false;
74     private boolean mHasReplyAction = false;
75     private boolean mReplyActionIsMocked = false;
76     private boolean mMarkAsReadActionIsMocked = false;
77     private boolean mPendingIntentIsMocked = false;
78     private boolean mHasReplyWrongSemanticAction = false;
79     private boolean mHasMarkAsRead = false;
80     private boolean mUseInvisibleAction = false;
81     private boolean mShowsUI = false;
82     private Instant mConnectionTime = Instant.now();
83     private String mContentTitle = "";
84     private String mCategory = "";
85     private String mContentText = "";
86     private Action mReplyAction = null;
87     private Action mMarkAsReadAction = null;
88     private PendingIntent mPendingIntent = null;
89 
MockMessageNotificationBuilder(Context context, String channelId, int smallIconId)90     public MockMessageNotificationBuilder(Context context, String channelId, int smallIconId) {
91         mContext = context;
92         mChannelId = channelId;
93         mSmallIconId = smallIconId;
94         mActionIcon = Icon.createWithResource(mContext, /* resId= */ 1);
95         mSender = (new Person.Builder()).setName(SENDER_NAME).build();
96         mUser = (new Person.Builder()).setName(USER_NAME).build();
97     }
98 
build()99     public Notification build() {
100         NotificationCompat.Builder builder = new NotificationCompat.Builder(this.mContext,
101                 mChannelId).setSmallIcon(mSmallIconId).setContentText(
102                 CONTENT_SUBJECT);
103 
104         if (mExtras != null) {
105             builder.setExtras(mExtras);
106         }
107 
108         if (!mContentTitle.isEmpty()) {
109             builder.setContentTitle(mContentTitle);
110         }
111 
112         if (!mContentText.isEmpty()) {
113             builder.setContentText(mContentText);
114         }
115 
116         if (!mCategory.isEmpty()) {
117             builder.setCategory(mCategory);
118         }
119 
120         if (mHasMessagingStyle) {
121             builder.setStyle(buildStyle());
122         }
123 
124         if (mPendingIntentIsMocked) {
125             mPendingIntent = mock(PendingIntent.class);
126             when(mPendingIntent.describeContents()).thenReturn(0);
127         } else {
128             mPendingIntent = PendingIntent.getService(mContext, /* requestCode= */ 101,
129                     new Intent(mContext, MockService.class), FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE);
130         }
131 
132         if (mHasReplyAction) {
133             if (mReplyActionIsMocked) {
134                 mReplyAction = getMockReplyAction(
135                         mHasReplyWrongSemanticAction ? Action.SEMANTIC_ACTION_NONE
136                                 : SEMANTIC_ACTION_REPLY);
137             } else {
138                 mReplyAction = getNonMockReplyAction(
139                         mHasReplyWrongSemanticAction ? Action.SEMANTIC_ACTION_NONE
140                                 : SEMANTIC_ACTION_REPLY);
141             }
142 
143             if (mUseInvisibleAction) {
144                 builder.addInvisibleAction(mReplyAction);
145             } else {
146                 builder.addAction(mReplyAction);
147             }
148         }
149 
150         if (mHasMarkAsRead) {
151             if (mMarkAsReadActionIsMocked) {
152                 mMarkAsReadAction = getMockMarkAsReadAction();
153             } else {
154                 mMarkAsReadAction = getNonMockMarkAsReadAction();
155             }
156 
157             if (mUseInvisibleAction) {
158                 builder.addInvisibleAction(mMarkAsReadAction);
159             } else {
160                 builder.addAction(mMarkAsReadAction);
161             }
162         }
163 
164         return builder.build();
165     }
166 
setHasMessagingStyle(boolean hasMessagingStyle)167     public MockMessageNotificationBuilder setHasMessagingStyle(boolean hasMessagingStyle) {
168         mHasMessagingStyle = hasMessagingStyle;
169         return this;
170     }
171 
setOldMessage(boolean oldMessage)172     public MockMessageNotificationBuilder setOldMessage(boolean oldMessage) {
173         mIsOldMessage = oldMessage;
174         return this;
175     }
176 
setHasReplyAction(boolean hasReplyAction)177     public MockMessageNotificationBuilder setHasReplyAction(boolean hasReplyAction) {
178         mHasReplyAction = hasReplyAction;
179         return this;
180     }
181 
setReplyActionIsMocked(boolean replyActionIsMocked)182     public MockMessageNotificationBuilder setReplyActionIsMocked(boolean replyActionIsMocked) {
183         mReplyActionIsMocked = replyActionIsMocked;
184         return this;
185     }
186 
setMarkAsReadActionIsMocked( boolean markAsReadActionIsMocked)187     public MockMessageNotificationBuilder setMarkAsReadActionIsMocked(
188             boolean markAsReadActionIsMocked) {
189         mMarkAsReadActionIsMocked = markAsReadActionIsMocked;
190         return this;
191     }
192 
setPendingIntentIsMocked(boolean pendingIntentIsMocked)193     public MockMessageNotificationBuilder setPendingIntentIsMocked(boolean pendingIntentIsMocked) {
194         mPendingIntentIsMocked = pendingIntentIsMocked;
195         return this;
196     }
197 
setHasReplyWrongSemanticAction( boolean hasReplyWrongSemanticAction)198     public MockMessageNotificationBuilder setHasReplyWrongSemanticAction(
199             boolean hasReplyWrongSemanticAction) {
200         mHasReplyWrongSemanticAction = hasReplyWrongSemanticAction;
201         return this;
202     }
203 
setHasMarkAsRead(boolean hasMarkAsRead)204     public MockMessageNotificationBuilder setHasMarkAsRead(boolean hasMarkAsRead) {
205         mHasMarkAsRead = hasMarkAsRead;
206         return this;
207     }
208 
setUseInvisibleAction(boolean useInvisibleAction)209     public MockMessageNotificationBuilder setUseInvisibleAction(boolean useInvisibleAction) {
210         mUseInvisibleAction = useInvisibleAction;
211         return this;
212     }
213 
setShowsUI(boolean showsUI)214     public MockMessageNotificationBuilder setShowsUI(boolean showsUI) {
215         mShowsUI = showsUI;
216         return this;
217     }
218 
setConnectionTime(Instant connectionTime)219     public MockMessageNotificationBuilder setConnectionTime(Instant connectionTime) {
220         mConnectionTime = connectionTime;
221         return this;
222     }
223 
setPostSpecificMessage( MessagingStyle.Message postSpecificMessage)224     public MockMessageNotificationBuilder setPostSpecificMessage(
225             MessagingStyle.Message postSpecificMessage) {
226         mPostSpecificMessage = postSpecificMessage;
227         return this;
228     }
229 
setContentTitle(String contentTitle)230     public MockMessageNotificationBuilder setContentTitle(String contentTitle) {
231         mContentTitle = contentTitle;
232         return this;
233     }
234 
setCategory(String category)235     public MockMessageNotificationBuilder setCategory(String category) {
236         mCategory = category;
237         return this;
238     }
239 
setContentText(String contentText)240     public MockMessageNotificationBuilder setContentText(String contentText) {
241         mContentText = contentText;
242         return this;
243     }
244 
setExtras(@ullable Bundle extras)245     public MockMessageNotificationBuilder setExtras(@Nullable Bundle extras) {
246         mExtras = extras;
247         return this;
248     }
249 
getReplyAction()250     public Action getReplyAction() {
251         return mReplyAction;
252     }
253 
getMarkAsReadAction()254     public Action getMarkAsReadAction() {
255         return mMarkAsReadAction;
256     }
257 
getPendingIntent()258     public PendingIntent getPendingIntent() {
259         return mPendingIntent;
260     }
261 
buildStyle()262     private MessagingStyle buildStyle() {
263         MessagingStyle builder = new MessagingStyle(mUser);
264         long connectionTimeMs = mConnectionTime.toEpochMilli();
265         if (mPostSpecificMessage != null) {
266             return builder.addMessage(mPostSpecificMessage);
267         } else if (mIsOldMessage) {
268             return builder.addMessage(OLD_MESSAGE, /* timestamp= */ connectionTimeMs - 100,
269                     mSender);
270         } else {
271             return builder.addMessage(FIRST_MESSAGE, /* timestamp= */ connectionTimeMs + 100,
272                     mSender).addMessage(SECOND_MESSAGE, /* timestamp= */ connectionTimeMs + 100,
273                     mSender);
274         }
275     }
276 
getNonMockReplyAction(int semanticAction)277     private Action getNonMockReplyAction(int semanticAction) {
278         return (new Action.Builder(IconCompat.createFromIcon(mActionIcon), TITLE_REPLY,
279                 mPendingIntent)).addRemoteInput(
280                 new RemoteInput.Builder(mChannelId).build()).setSemanticAction(
281                 semanticAction).setShowsUserInterface(mShowsUI).build();
282     }
283 
getMockReplyAction(int semanticAction)284     private Action getMockReplyAction(int semanticAction) {
285         IconCompat iconCompat = IconCompat.createFromIcon(mActionIcon);
286         Action action = mock(Action.class);
287         when(action.actionIntent).thenReturn(mPendingIntent);
288         when(action.title).thenReturn(TITLE_REPLY);
289         when(action.icon).thenReturn(iconCompat.getResId());
290         when(action.getActionIntent()).thenReturn(mPendingIntent);
291         when(action.getAllowGeneratedReplies()).thenReturn(true);
292         when(action.getDataOnlyRemoteInputs()).thenReturn(null);
293         when(action.getExtras()).thenReturn(new Bundle());
294         when(action.getRemoteInputs()).thenReturn(new RemoteInput[]{new RemoteInput.Builder(
295                 mChannelId).build()});
296         when(action.getSemanticAction()).thenReturn(semanticAction);
297         when(action.getShowsUserInterface()).thenReturn(mShowsUI);
298         when(action.getTitle()).thenReturn(TITLE_REPLY);
299         when(action.isContextual()).thenReturn(false);
300         when(action.getIconCompat()).thenReturn(iconCompat);
301         return action;
302     }
303 
getNonMockMarkAsReadAction()304     private Action getNonMockMarkAsReadAction() {
305         return (new Action.Builder(IconCompat.createFromIcon(mActionIcon),
306                 TITLE_MARK_AS_READ, mPendingIntent)).addRemoteInput(
307                 new RemoteInput.Builder(mChannelId).build()).setSemanticAction(
308                 SEMANTIC_ACTION_MARK_AS_READ).setShowsUserInterface(false).build();
309     }
310 
getMockMarkAsReadAction()311     private Action getMockMarkAsReadAction() {
312         IconCompat iconCompat = IconCompat.createFromIcon(mActionIcon);
313         Action action = mock(Action.class);
314         when(action.actionIntent).thenReturn(mPendingIntent);
315         when(action.title).thenReturn(TITLE_MARK_AS_READ);
316         when(action.icon).thenReturn(iconCompat.getResId());
317         when(action.getActionIntent()).thenReturn(mPendingIntent);
318         when(action.getAllowGeneratedReplies()).thenReturn(true);
319         when(action.getDataOnlyRemoteInputs()).thenReturn(null);
320         when(action.getExtras()).thenReturn(new Bundle());
321         when(action.getRemoteInputs()).thenReturn(new RemoteInput[]{new RemoteInput.Builder(
322                 mChannelId).build()});
323         when(action.getSemanticAction()).thenReturn(SEMANTIC_ACTION_MARK_AS_READ);
324         when(action.getShowsUserInterface()).thenReturn(false);
325         when(action.getTitle()).thenReturn(TITLE_MARK_AS_READ);
326         when(action.isContextual()).thenReturn(false);
327         when(action.getIconCompat()).thenReturn(iconCompat);
328         return action;
329     }
330 
331     private static class MockService extends Service {
332         @Nullable
333         @Override
onBind(Intent intent)334         public IBinder onBind(Intent intent) {
335             return null;
336         }
337     }
338 }