blob: c17aa925480881415ed6654c9dbb75410d5b7bec [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;
24import static org.junit.Assert.assertSame;
Selim Cinek389edcd2017-05-11 19:16:44 -070025import static org.junit.Assert.assertTrue;
26
Kodlee Yinc72c44d2017-12-21 22:07:15 +000027import android.annotation.Nullable;
Selim Cinek389edcd2017-05-11 19:16:44 -070028import android.content.Context;
Adrian Roosfb921842017-10-26 14:49:56 +020029import android.content.Intent;
Robin Leead7e72a2017-12-04 15:45:46 +010030import android.graphics.BitmapFactory;
31import android.graphics.drawable.Icon;
Selim Cinek389edcd2017-05-11 19:16:44 -070032import android.media.session.MediaSession;
Kodlee Yin14656422017-12-22 17:00:46 -080033import android.os.Build;
Adrian Roosfb921842017-10-26 14:49:56 +020034import android.os.Parcel;
Robin Leead7e72a2017-12-04 15:45:46 +010035import android.os.Parcelable;
Adrian Roosfb921842017-10-26 14:49:56 +020036import android.widget.RemoteViews;
Selim Cinek389edcd2017-05-11 19:16:44 -070037
Tadashi G. Takaokab4470f22019-01-15 18:29:15 +090038import androidx.test.InstrumentationRegistry;
39import androidx.test.filters.SmallTest;
40import androidx.test.runner.AndroidJUnit4;
41
Selim Cinek389edcd2017-05-11 19:16:44 -070042import org.junit.Before;
43import org.junit.Test;
44import org.junit.runner.RunWith;
45
Kodlee Yinc72c44d2017-12-21 22:07:15 +000046import java.util.function.Consumer;
47
Selim Cinek389edcd2017-05-11 19:16:44 -070048@RunWith(AndroidJUnit4.class)
49@SmallTest
50public class NotificationTest {
51
52 private Context mContext;
53
54 @Before
55 public void setUp() {
56 mContext = InstrumentationRegistry.getContext();
57 }
58
59 @Test
Julia Reynoldsd71c5a92017-06-30 13:34:01 -040060 public void testColorizedByPermission() {
61 Notification n = new Notification.Builder(mContext, "test")
62 .setFlag(Notification.FLAG_CAN_COLORIZE, true)
63 .setColorized(true)
64 .build();
65 assertTrue(n.isColorized());
66
67 n = new Notification.Builder(mContext, "test")
68 .setFlag(Notification.FLAG_CAN_COLORIZE, true)
69 .build();
70 assertFalse(n.isColorized());
71
72 n = new Notification.Builder(mContext, "test")
73 .setFlag(Notification.FLAG_CAN_COLORIZE, false)
74 .setColorized(true)
75 .build();
76 assertFalse(n.isColorized());
77 }
78
79 @Test
80 public void testColorizedByForeground() {
81 Notification n = new Notification.Builder(mContext, "test")
82 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
83 .setColorized(true)
84 .build();
85 assertTrue(n.isColorized());
86
87 n = new Notification.Builder(mContext, "test")
88 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
89 .build();
90 assertFalse(n.isColorized());
91
92 n = new Notification.Builder(mContext, "test")
93 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, false)
94 .setColorized(true)
95 .build();
96 assertFalse(n.isColorized());
97 }
98
99 @Test
Selim Cinek389edcd2017-05-11 19:16:44 -0700100 public void testColorSatisfiedWhenBgDarkTextDarker() {
101 Notification.Builder builder = getMediaNotification();
Julia Reynoldsd71c5a92017-06-30 13:34:01 -0400102 Notification n = builder.build();
103
104 assertTrue(n.isColorized());
Selim Cinek389edcd2017-05-11 19:16:44 -0700105
106 // An initial guess where the foreground color is actually darker than an already dark bg
107 int backgroundColor = 0xff585868;
108 int initialForegroundColor = 0xff505868;
109 builder.setColorPalette(backgroundColor, initialForegroundColor);
Selim Cinek10bbc0e2018-11-21 15:14:10 -0800110 int primaryTextColor = builder.getPrimaryTextColor(builder.mParams);
Selim Cinek389edcd2017-05-11 19:16:44 -0700111 assertTrue(satisfiesTextContrast(primaryTextColor, backgroundColor));
Selim Cinek10bbc0e2018-11-21 15:14:10 -0800112 int secondaryTextColor = builder.getSecondaryTextColor(builder.mParams);
Selim Cinek389edcd2017-05-11 19:16:44 -0700113 assertTrue(satisfiesTextContrast(secondaryTextColor, backgroundColor));
114 }
115
Julia Reynolds6ad0aec2017-07-05 08:47:03 -0400116 @Test
117 public void testHasCompletedProgress_noProgress() {
118 Notification n = new Notification.Builder(mContext).build();
119
120 assertFalse(n.hasCompletedProgress());
121 }
122
123 @Test
124 public void testHasCompletedProgress_complete() {
125 Notification n = new Notification.Builder(mContext)
126 .setProgress(100, 100, true)
127 .build();
128 Notification n2 = new Notification.Builder(mContext)
129 .setProgress(10, 10, false)
130 .build();
131 assertTrue(n.hasCompletedProgress());
132 assertTrue(n2.hasCompletedProgress());
133 }
134
135 @Test
136 public void testHasCompletedProgress_notComplete() {
137 Notification n = new Notification.Builder(mContext)
138 .setProgress(100, 99, true)
139 .build();
140 Notification n2 = new Notification.Builder(mContext)
141 .setProgress(10, 4, false)
142 .build();
143 assertFalse(n.hasCompletedProgress());
144 assertFalse(n2.hasCompletedProgress());
145 }
146
147 @Test
148 public void testHasCompletedProgress_zeroMax() {
149 Notification n = new Notification.Builder(mContext)
150 .setProgress(0, 0, true)
151 .build();
152 assertFalse(n.hasCompletedProgress());
153 }
154
Adrian Roosfb921842017-10-26 14:49:56 +0200155 @Test
Robin Leead7e72a2017-12-04 15:45:46 +0100156 public void largeIconMultipleReferences_keptAfterParcelling() {
157 Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource(
158 mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96));
159
160 Notification n = new Notification.Builder(mContext).setLargeIcon(originalIcon).build();
161 assertSame(n.getLargeIcon(), originalIcon);
162
163 Notification q = writeAndReadParcelable(n);
164 assertNotSame(q.getLargeIcon(), n.getLargeIcon());
165
166 assertTrue(q.getLargeIcon().getBitmap().sameAs(n.getLargeIcon().getBitmap()));
167 assertSame(q.getLargeIcon(), q.extras.getParcelable(Notification.EXTRA_LARGE_ICON));
168 }
169
170 @Test
171 public void largeIconReferenceInExtrasOnly_keptAfterParcelling() {
172 Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource(
173 mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96));
174
175 Notification n = new Notification.Builder(mContext).build();
176 n.extras.putParcelable(Notification.EXTRA_LARGE_ICON, originalIcon);
177 assertSame(n.getLargeIcon(), null);
178
179 Notification q = writeAndReadParcelable(n);
180 assertSame(q.getLargeIcon(), null);
181 assertTrue(((Icon) q.extras.getParcelable(Notification.EXTRA_LARGE_ICON)).getBitmap()
182 .sameAs(originalIcon.getBitmap()));
183 }
184
185 @Test
Adrian Roosfb921842017-10-26 14:49:56 +0200186 public void allPendingIntents_recollectedAfterReusingBuilder() {
187 PendingIntent intent1 = PendingIntent.getActivity(mContext, 0, new Intent("test1"), 0);
188 PendingIntent intent2 = PendingIntent.getActivity(mContext, 0, new Intent("test2"), 0);
189
190 Notification.Builder builder = new Notification.Builder(mContext, "channel");
191 builder.setContentIntent(intent1);
192
193 Parcel p = Parcel.obtain();
194
195 Notification n1 = builder.build();
196 n1.writeToParcel(p, 0);
197
198 builder.setContentIntent(intent2);
199 Notification n2 = builder.build();
200 n2.writeToParcel(p, 0);
201
202 assertTrue(n2.allPendingIntents.contains(intent2));
203 }
204
205 @Test
206 public void allPendingIntents_containsCustomRemoteViews() {
207 PendingIntent intent = PendingIntent.getActivity(mContext, 0, new Intent("test"), 0);
208
209 RemoteViews contentView = new RemoteViews(mContext.getPackageName(), 0 /* layoutId */);
210 contentView.setOnClickPendingIntent(1 /* id */, intent);
211
212 Notification.Builder builder = new Notification.Builder(mContext, "channel");
213 builder.setCustomContentView(contentView);
214
215 Parcel p = Parcel.obtain();
216
217 Notification n = builder.build();
218 n.writeToParcel(p, 0);
219
220 assertTrue(n.allPendingIntents.contains(intent));
221 }
222
Kodlee Yin9ac617c2017-12-19 11:20:50 -0800223 @Test
Kodlee Yin14656422017-12-22 17:00:46 -0800224 public void messagingStyle_isGroupConversation() {
225 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
Kodlee Yin9ac617c2017-12-19 11:20:50 -0800226 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
Kodlee Yin14656422017-12-22 17:00:46 -0800227 .setGroupConversation(true)
228 .setConversationTitle("test conversation title");
Kodlee Yin9ac617c2017-12-19 11:20:50 -0800229 Notification notification = new Notification.Builder(mContext, "test id")
230 .setSmallIcon(1)
231 .setContentTitle("test title")
232 .setStyle(messagingStyle)
233 .build();
234
235 assertTrue(messagingStyle.isGroupConversation());
236 assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
237 }
238
Kodlee Yin14656422017-12-22 17:00:46 -0800239 @Test
240 public void messagingStyle_isGroupConversation_noConversationTitle() {
241 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
242 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
243 .setGroupConversation(true)
244 .setConversationTitle(null);
245 Notification notification = new Notification.Builder(mContext, "test id")
246 .setSmallIcon(1)
247 .setContentTitle("test title")
248 .setStyle(messagingStyle)
249 .build();
250
251 assertTrue(messagingStyle.isGroupConversation());
252 assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
253 }
254
255 @Test
256 public void messagingStyle_isGroupConversation_withConversationTitle_legacy() {
257 // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
258 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
259 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
260 .setGroupConversation(false)
261 .setConversationTitle("test conversation title");
262 Notification notification = new Notification.Builder(mContext, "test id")
263 .setSmallIcon(1)
264 .setContentTitle("test title")
265 .setStyle(messagingStyle)
266 .build();
267
268 assertTrue(messagingStyle.isGroupConversation());
269 assertFalse(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
270 }
271
272 @Test
273 public void messagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
274 // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
275 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
276 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
277 .setGroupConversation(true)
278 .setConversationTitle(null);
279 Notification notification = new Notification.Builder(mContext, "test id")
280 .setSmallIcon(1)
281 .setContentTitle("test title")
282 .setStyle(messagingStyle)
283 .build();
284
285 assertFalse(messagingStyle.isGroupConversation());
286 assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
287 }
288
Kodlee Yinc72c44d2017-12-21 22:07:15 +0000289 @Test
290 public void action_builder_hasDefault() {
291 Notification.Action action = makeNotificationAction(null);
292 assertEquals(Notification.Action.SEMANTIC_ACTION_NONE, action.getSemanticAction());
293 }
294
295 @Test
296 public void action_builder_setSemanticAction() {
297 Notification.Action action = makeNotificationAction(
298 builder -> builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_REPLY));
299 assertEquals(Notification.Action.SEMANTIC_ACTION_REPLY, action.getSemanticAction());
300 }
301
302 @Test
303 public void action_parcel() {
304 Notification.Action action = writeAndReadParcelable(
305 makeNotificationAction(builder -> {
306 builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_ARCHIVE);
307 builder.setAllowGeneratedReplies(true);
308 }));
309
310 assertEquals(Notification.Action.SEMANTIC_ACTION_ARCHIVE, action.getSemanticAction());
311 assertTrue(action.getAllowGeneratedReplies());
312 }
313
314 @Test
315 public void action_clone() {
316 Notification.Action action = makeNotificationAction(
317 builder -> builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_DELETE));
318 assertEquals(
319 Notification.Action.SEMANTIC_ACTION_DELETE,
320 action.clone().getSemanticAction());
321 }
322
Selim Cinek389edcd2017-05-11 19:16:44 -0700323 private Notification.Builder getMediaNotification() {
324 MediaSession session = new MediaSession(mContext, "test");
325 return new Notification.Builder(mContext, "color")
326 .setSmallIcon(com.android.internal.R.drawable.emergency_icon)
327 .setContentTitle("Title")
328 .setContentText("Text")
329 .setStyle(new Notification.MediaStyle().setMediaSession(session.getSessionToken()));
330 }
Robin Leead7e72a2017-12-04 15:45:46 +0100331
332 /**
333 * Writes an arbitrary {@link Parcelable} into a {@link Parcel} using its writeToParcel
334 * method before reading it out again to check that it was sent properly.
335 */
336 private static <T extends Parcelable> T writeAndReadParcelable(T original) {
337 Parcel p = Parcel.obtain();
338 p.writeParcelable(original, /* flags */ 0);
339 p.setDataPosition(0);
340 return p.readParcelable(/* classLoader */ null);
341 }
Kodlee Yinc72c44d2017-12-21 22:07:15 +0000342
343 /**
344 * Creates a Notification.Action by mocking initial dependencies and then applying
345 * transformations if they're defined.
346 */
347 private Notification.Action makeNotificationAction(
348 @Nullable Consumer<Notification.Action.Builder> transformation) {
349 Notification.Action.Builder actionBuilder =
350 new Notification.Action.Builder(null, "Test Title", null);
351 if (transformation != null) {
352 transformation.accept(actionBuilder);
353 }
354 return actionBuilder.build();
355 }
Selim Cinek389edcd2017-05-11 19:16:44 -0700356}