blob: 109cff9a7d575f1ad70a5cea236e0a126b185747 [file] [log] [blame]
Selim Cinek389edcd2017-05-11 19:16:44 -07001/*
2 * Copyright (C) 2017 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
17package android.app;
18
Lucas Dupina291d192018-06-07 13:59:42 -070019import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
Selim Cinek389edcd2017-05-11 19:16:44 -070020
Kodlee Yinc72c44d2017-12-21 22:07:15 +000021import static org.junit.Assert.assertEquals;
Julia Reynoldsd71c5a92017-06-30 13:34:01 -040022import static org.junit.Assert.assertFalse;
Robin Leead7e72a2017-12-04 15:45:46 +010023import static org.junit.Assert.assertNotSame;
Felipe Leme90205ef2019-03-05 09:59:52 -080024import static org.junit.Assert.assertNull;
Robin Leead7e72a2017-12-04 15:45:46 +010025import static org.junit.Assert.assertSame;
Selim Cinek389edcd2017-05-11 19:16:44 -070026import static org.junit.Assert.assertTrue;
27
Kodlee Yinc72c44d2017-12-21 22:07:15 +000028import android.annotation.Nullable;
Selim Cinek389edcd2017-05-11 19:16:44 -070029import android.content.Context;
Adrian Roosfb921842017-10-26 14:49:56 +020030import android.content.Intent;
Felipe Leme90205ef2019-03-05 09:59:52 -080031import android.content.LocusId;
Robin Leead7e72a2017-12-04 15:45:46 +010032import android.graphics.BitmapFactory;
33import android.graphics.drawable.Icon;
Selim Cinek389edcd2017-05-11 19:16:44 -070034import android.media.session.MediaSession;
Kodlee Yin14656422017-12-22 17:00:46 -080035import android.os.Build;
Adrian Roosfb921842017-10-26 14:49:56 +020036import android.os.Parcel;
Robin Leead7e72a2017-12-04 15:45:46 +010037import android.os.Parcelable;
Adrian Roosfb921842017-10-26 14:49:56 +020038import android.widget.RemoteViews;
Selim Cinek389edcd2017-05-11 19:16:44 -070039
Tadashi G. Takaokab4470f22019-01-15 18:29:15 +090040import androidx.test.InstrumentationRegistry;
41import androidx.test.filters.SmallTest;
42import androidx.test.runner.AndroidJUnit4;
43
Selim Cinek389edcd2017-05-11 19:16:44 -070044import org.junit.Before;
45import org.junit.Test;
46import org.junit.runner.RunWith;
47
Kodlee Yinc72c44d2017-12-21 22:07:15 +000048import java.util.function.Consumer;
49
Selim Cinek389edcd2017-05-11 19:16:44 -070050@RunWith(AndroidJUnit4.class)
51@SmallTest
52public class NotificationTest {
53
54 private Context mContext;
55
56 @Before
57 public void setUp() {
58 mContext = InstrumentationRegistry.getContext();
59 }
60
61 @Test
Julia Reynoldsd71c5a92017-06-30 13:34:01 -040062 public void testColorizedByPermission() {
63 Notification n = new Notification.Builder(mContext, "test")
64 .setFlag(Notification.FLAG_CAN_COLORIZE, true)
65 .setColorized(true)
66 .build();
67 assertTrue(n.isColorized());
68
69 n = new Notification.Builder(mContext, "test")
70 .setFlag(Notification.FLAG_CAN_COLORIZE, true)
71 .build();
72 assertFalse(n.isColorized());
73
74 n = new Notification.Builder(mContext, "test")
75 .setFlag(Notification.FLAG_CAN_COLORIZE, false)
76 .setColorized(true)
77 .build();
78 assertFalse(n.isColorized());
79 }
80
81 @Test
82 public void testColorizedByForeground() {
83 Notification n = new Notification.Builder(mContext, "test")
84 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
85 .setColorized(true)
86 .build();
87 assertTrue(n.isColorized());
88
89 n = new Notification.Builder(mContext, "test")
90 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
91 .build();
92 assertFalse(n.isColorized());
93
94 n = new Notification.Builder(mContext, "test")
95 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, false)
96 .setColorized(true)
97 .build();
98 assertFalse(n.isColorized());
99 }
100
101 @Test
Selim Cinek389edcd2017-05-11 19:16:44 -0700102 public void testColorSatisfiedWhenBgDarkTextDarker() {
103 Notification.Builder builder = getMediaNotification();
Julia Reynoldsd71c5a92017-06-30 13:34:01 -0400104 Notification n = builder.build();
105
106 assertTrue(n.isColorized());
Selim Cinek389edcd2017-05-11 19:16:44 -0700107
108 // An initial guess where the foreground color is actually darker than an already dark bg
109 int backgroundColor = 0xff585868;
110 int initialForegroundColor = 0xff505868;
111 builder.setColorPalette(backgroundColor, initialForegroundColor);
Selim Cinek10bbc0e2018-11-21 15:14:10 -0800112 int primaryTextColor = builder.getPrimaryTextColor(builder.mParams);
Selim Cinek389edcd2017-05-11 19:16:44 -0700113 assertTrue(satisfiesTextContrast(primaryTextColor, backgroundColor));
Selim Cinek10bbc0e2018-11-21 15:14:10 -0800114 int secondaryTextColor = builder.getSecondaryTextColor(builder.mParams);
Selim Cinek389edcd2017-05-11 19:16:44 -0700115 assertTrue(satisfiesTextContrast(secondaryTextColor, backgroundColor));
116 }
117
Julia Reynolds6ad0aec2017-07-05 08:47:03 -0400118 @Test
119 public void testHasCompletedProgress_noProgress() {
120 Notification n = new Notification.Builder(mContext).build();
121
122 assertFalse(n.hasCompletedProgress());
123 }
124
125 @Test
126 public void testHasCompletedProgress_complete() {
127 Notification n = new Notification.Builder(mContext)
128 .setProgress(100, 100, true)
129 .build();
130 Notification n2 = new Notification.Builder(mContext)
131 .setProgress(10, 10, false)
132 .build();
133 assertTrue(n.hasCompletedProgress());
134 assertTrue(n2.hasCompletedProgress());
135 }
136
137 @Test
138 public void testHasCompletedProgress_notComplete() {
139 Notification n = new Notification.Builder(mContext)
140 .setProgress(100, 99, true)
141 .build();
142 Notification n2 = new Notification.Builder(mContext)
143 .setProgress(10, 4, false)
144 .build();
145 assertFalse(n.hasCompletedProgress());
146 assertFalse(n2.hasCompletedProgress());
147 }
148
149 @Test
150 public void testHasCompletedProgress_zeroMax() {
151 Notification n = new Notification.Builder(mContext)
152 .setProgress(0, 0, true)
153 .build();
154 assertFalse(n.hasCompletedProgress());
155 }
156
Adrian Roosfb921842017-10-26 14:49:56 +0200157 @Test
Robin Leead7e72a2017-12-04 15:45:46 +0100158 public void largeIconMultipleReferences_keptAfterParcelling() {
159 Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource(
160 mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96));
161
162 Notification n = new Notification.Builder(mContext).setLargeIcon(originalIcon).build();
163 assertSame(n.getLargeIcon(), originalIcon);
164
165 Notification q = writeAndReadParcelable(n);
166 assertNotSame(q.getLargeIcon(), n.getLargeIcon());
167
168 assertTrue(q.getLargeIcon().getBitmap().sameAs(n.getLargeIcon().getBitmap()));
169 assertSame(q.getLargeIcon(), q.extras.getParcelable(Notification.EXTRA_LARGE_ICON));
170 }
171
172 @Test
173 public void largeIconReferenceInExtrasOnly_keptAfterParcelling() {
174 Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource(
175 mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96));
176
177 Notification n = new Notification.Builder(mContext).build();
178 n.extras.putParcelable(Notification.EXTRA_LARGE_ICON, originalIcon);
179 assertSame(n.getLargeIcon(), null);
180
181 Notification q = writeAndReadParcelable(n);
182 assertSame(q.getLargeIcon(), null);
183 assertTrue(((Icon) q.extras.getParcelable(Notification.EXTRA_LARGE_ICON)).getBitmap()
184 .sameAs(originalIcon.getBitmap()));
185 }
186
187 @Test
Adrian Roosfb921842017-10-26 14:49:56 +0200188 public void allPendingIntents_recollectedAfterReusingBuilder() {
189 PendingIntent intent1 = PendingIntent.getActivity(mContext, 0, new Intent("test1"), 0);
190 PendingIntent intent2 = PendingIntent.getActivity(mContext, 0, new Intent("test2"), 0);
191
192 Notification.Builder builder = new Notification.Builder(mContext, "channel");
193 builder.setContentIntent(intent1);
194
195 Parcel p = Parcel.obtain();
196
197 Notification n1 = builder.build();
198 n1.writeToParcel(p, 0);
199
200 builder.setContentIntent(intent2);
201 Notification n2 = builder.build();
202 n2.writeToParcel(p, 0);
203
204 assertTrue(n2.allPendingIntents.contains(intent2));
205 }
206
207 @Test
208 public void allPendingIntents_containsCustomRemoteViews() {
209 PendingIntent intent = PendingIntent.getActivity(mContext, 0, new Intent("test"), 0);
210
211 RemoteViews contentView = new RemoteViews(mContext.getPackageName(), 0 /* layoutId */);
212 contentView.setOnClickPendingIntent(1 /* id */, intent);
213
214 Notification.Builder builder = new Notification.Builder(mContext, "channel");
215 builder.setCustomContentView(contentView);
216
217 Parcel p = Parcel.obtain();
218
219 Notification n = builder.build();
220 n.writeToParcel(p, 0);
221
222 assertTrue(n.allPendingIntents.contains(intent));
223 }
224
Kodlee Yin9ac617c2017-12-19 11:20:50 -0800225 @Test
Kodlee Yin14656422017-12-22 17:00:46 -0800226 public void messagingStyle_isGroupConversation() {
227 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
Kodlee Yin9ac617c2017-12-19 11:20:50 -0800228 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
Kodlee Yin14656422017-12-22 17:00:46 -0800229 .setGroupConversation(true)
230 .setConversationTitle("test conversation title");
Kodlee Yin9ac617c2017-12-19 11:20:50 -0800231 Notification notification = new Notification.Builder(mContext, "test id")
232 .setSmallIcon(1)
233 .setContentTitle("test title")
234 .setStyle(messagingStyle)
235 .build();
236
237 assertTrue(messagingStyle.isGroupConversation());
238 assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
239 }
240
Kodlee Yin14656422017-12-22 17:00:46 -0800241 @Test
242 public void messagingStyle_isGroupConversation_noConversationTitle() {
243 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
244 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
245 .setGroupConversation(true)
246 .setConversationTitle(null);
247 Notification notification = new Notification.Builder(mContext, "test id")
248 .setSmallIcon(1)
249 .setContentTitle("test title")
250 .setStyle(messagingStyle)
251 .build();
252
253 assertTrue(messagingStyle.isGroupConversation());
254 assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
255 }
256
257 @Test
258 public void messagingStyle_isGroupConversation_withConversationTitle_legacy() {
259 // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
260 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
261 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
262 .setGroupConversation(false)
263 .setConversationTitle("test conversation title");
264 Notification notification = new Notification.Builder(mContext, "test id")
265 .setSmallIcon(1)
266 .setContentTitle("test title")
267 .setStyle(messagingStyle)
268 .build();
269
270 assertTrue(messagingStyle.isGroupConversation());
271 assertFalse(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
272 }
273
274 @Test
275 public void messagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
276 // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
277 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
278 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
279 .setGroupConversation(true)
280 .setConversationTitle(null);
281 Notification notification = new Notification.Builder(mContext, "test id")
282 .setSmallIcon(1)
283 .setContentTitle("test title")
284 .setStyle(messagingStyle)
285 .build();
286
287 assertFalse(messagingStyle.isGroupConversation());
288 assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
289 }
290
Kodlee Yinc72c44d2017-12-21 22:07:15 +0000291 @Test
292 public void action_builder_hasDefault() {
293 Notification.Action action = makeNotificationAction(null);
294 assertEquals(Notification.Action.SEMANTIC_ACTION_NONE, action.getSemanticAction());
295 }
296
297 @Test
298 public void action_builder_setSemanticAction() {
299 Notification.Action action = makeNotificationAction(
300 builder -> builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_REPLY));
301 assertEquals(Notification.Action.SEMANTIC_ACTION_REPLY, action.getSemanticAction());
302 }
303
304 @Test
305 public void action_parcel() {
306 Notification.Action action = writeAndReadParcelable(
307 makeNotificationAction(builder -> {
308 builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_ARCHIVE);
309 builder.setAllowGeneratedReplies(true);
310 }));
311
312 assertEquals(Notification.Action.SEMANTIC_ACTION_ARCHIVE, action.getSemanticAction());
313 assertTrue(action.getAllowGeneratedReplies());
314 }
315
316 @Test
317 public void action_clone() {
318 Notification.Action action = makeNotificationAction(
319 builder -> builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_DELETE));
320 assertEquals(
321 Notification.Action.SEMANTIC_ACTION_DELETE,
322 action.clone().getSemanticAction());
323 }
324
Felipe Leme90205ef2019-03-05 09:59:52 -0800325 @Test
326 public void testBuilder_setLocusId() {
327 LocusId locusId = new LocusId("4815162342");
328 Notification notification = new Notification.Builder(mContext, "whatever")
329 .setLocusId(locusId).build();
330 assertEquals(locusId, notification.getLocusId());
331
332 Notification clone = writeAndReadParcelable(notification);
333 assertEquals(locusId, clone.getLocusId());
334 }
335
336 @Test
337 public void testBuilder_setLocusId_null() {
338 Notification notification = new Notification.Builder(mContext, "whatever")
339 .setLocusId(null).build();
340 assertNull(notification.getLocusId());
341
342 Notification clone = writeAndReadParcelable(notification);
343 assertNull(clone.getLocusId());
344 }
345
Selim Cinek389edcd2017-05-11 19:16:44 -0700346 private Notification.Builder getMediaNotification() {
347 MediaSession session = new MediaSession(mContext, "test");
348 return new Notification.Builder(mContext, "color")
349 .setSmallIcon(com.android.internal.R.drawable.emergency_icon)
350 .setContentTitle("Title")
351 .setContentText("Text")
352 .setStyle(new Notification.MediaStyle().setMediaSession(session.getSessionToken()));
353 }
Robin Leead7e72a2017-12-04 15:45:46 +0100354
355 /**
356 * Writes an arbitrary {@link Parcelable} into a {@link Parcel} using its writeToParcel
357 * method before reading it out again to check that it was sent properly.
358 */
359 private static <T extends Parcelable> T writeAndReadParcelable(T original) {
360 Parcel p = Parcel.obtain();
361 p.writeParcelable(original, /* flags */ 0);
362 p.setDataPosition(0);
363 return p.readParcelable(/* classLoader */ null);
364 }
Kodlee Yinc72c44d2017-12-21 22:07:15 +0000365
366 /**
367 * Creates a Notification.Action by mocking initial dependencies and then applying
368 * transformations if they're defined.
369 */
370 private Notification.Action makeNotificationAction(
371 @Nullable Consumer<Notification.Action.Builder> transformation) {
372 Notification.Action.Builder actionBuilder =
373 new Notification.Action.Builder(null, "Test Title", null);
374 if (transformation != null) {
375 transformation.accept(actionBuilder);
376 }
377 return actionBuilder.build();
378 }
Selim Cinek389edcd2017-05-11 19:16:44 -0700379}