1/*
2 * Copyright (C) 2016 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.support.v4.app;
18
19import static android.support.v4.app.NotificationCompat.DEFAULT_ALL;
20import static android.support.v4.app.NotificationCompat.DEFAULT_LIGHTS;
21import static android.support.v4.app.NotificationCompat.DEFAULT_SOUND;
22import static android.support.v4.app.NotificationCompat.DEFAULT_VIBRATE;
23import static android.support.v4.app.NotificationCompat.GROUP_ALERT_ALL;
24import static android.support.v4.app.NotificationCompat.GROUP_ALERT_CHILDREN;
25import static android.support.v4.app.NotificationCompat.GROUP_ALERT_SUMMARY;
26
27import static org.junit.Assert.assertEquals;
28import static org.junit.Assert.assertFalse;
29import static org.junit.Assert.assertNotNull;
30import static org.junit.Assert.assertNull;
31import static org.junit.Assert.assertSame;
32import static org.junit.Assert.assertTrue;
33
34import android.annotation.TargetApi;
35import android.app.Notification;
36import android.content.Context;
37import android.net.Uri;
38import android.os.Build;
39import android.os.Bundle;
40import android.support.test.filters.SdkSuppress;
41import android.support.test.filters.SmallTest;
42import android.support.test.runner.AndroidJUnit4;
43import android.support.v4.BaseInstrumentationTestCase;
44import android.support.v4.os.BuildCompat;
45
46import org.junit.Before;
47import org.junit.Test;
48import org.junit.runner.RunWith;
49
50import java.util.ArrayList;
51
52
53@RunWith(AndroidJUnit4.class)
54@SmallTest
55public class NotificationCompatTest extends BaseInstrumentationTestCase<TestSupportActivity> {
56    private static final String TEXT_RESULT_KEY = "text";
57    private static final String DATA_RESULT_KEY = "data";
58    private static final String EXTRA_COLORIZED = "android.colorized";
59
60    Context mContext;
61
62    public NotificationCompatTest() {
63        super(TestSupportActivity.class);
64    }
65
66    @Before
67    public void setup() {
68        mContext = mActivityTestRule.getActivity();
69    }
70
71    @Test
72    public void testBadgeIcon() throws Throwable {
73        int badgeIcon = NotificationCompat.BADGE_ICON_SMALL;
74        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
75                .setBadgeIconType(badgeIcon)
76                .build();
77        if (BuildCompat.isAtLeastO()) {
78            assertEquals(badgeIcon, NotificationCompat.getBadgeIconType(n));
79        } else {
80            assertEquals(NotificationCompat.BADGE_ICON_NONE,
81                    NotificationCompat.getBadgeIconType(n));
82        }
83    }
84
85    @Test
86    public void testTimeout() throws Throwable {
87        long timeout = 23552;
88        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
89                .setTimeout(timeout)
90                .build();
91        if (BuildCompat.isAtLeastO()) {
92            assertEquals(timeout, NotificationCompat.getTimeout(n));
93        } else {
94            assertEquals(0, NotificationCompat.getTimeout(n));
95        }
96    }
97
98    @Test
99    public void testShortcutId() throws Throwable {
100        String shortcutId = "fgdfg";
101        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
102                .setShortcutId(shortcutId)
103                .build();
104        if (BuildCompat.isAtLeastO()) {
105            assertEquals(shortcutId, NotificationCompat.getShortcutId(n));
106        } else {
107            assertEquals(null, NotificationCompat.getShortcutId(n));
108        }
109    }
110
111    @Test
112    public void testNotificationChannel() throws Throwable {
113        String channelId = "new ID";
114        Notification n  = new NotificationCompat.Builder(mActivityTestRule.getActivity())
115                .setChannel(channelId)
116                .build();
117        if (BuildCompat.isAtLeastO()) {
118            assertEquals(channelId, NotificationCompat.getChannel(n));
119        } else {
120            assertNull(NotificationCompat.getChannel(n));
121        }
122    }
123
124    @Test
125    public void testNotificationChannel_assignedFromBuilder() throws Throwable {
126        String channelId = "new ID";
127        Notification n  = new NotificationCompat.Builder(mActivityTestRule.getActivity(), channelId)
128                .build();
129        if (BuildCompat.isAtLeastO()) {
130            assertEquals(channelId, NotificationCompat.getChannel(n));
131        } else {
132            assertNull(NotificationCompat.getChannel(n));
133        }
134    }
135
136    @Test
137    public void testNotificationActionBuilder_assignsColorized() throws Throwable {
138        Notification n = newNotificationBuilder().setColorized(true).build();
139        if (BuildCompat.isAtLeastO()) {
140            Bundle extras = NotificationCompat.getExtras(n);
141            assertTrue(Boolean.TRUE.equals(extras.get(EXTRA_COLORIZED)));
142        }
143    }
144
145    @Test
146    public void testNotificationActionBuilder_unassignesColorized() throws Throwable {
147        Notification n = newNotificationBuilder().setColorized(false).build();
148        if (BuildCompat.isAtLeastO()) {
149            Bundle extras = NotificationCompat.getExtras(n);
150            assertTrue(Boolean.FALSE.equals(extras.get(EXTRA_COLORIZED)));
151        }
152    }
153
154    @Test
155    public void testNotificationActionBuilder_doesntAssignColorized() throws Throwable {
156        Notification n = newNotificationBuilder().build();
157        if (BuildCompat.isAtLeastO()) {
158            Bundle extras = NotificationCompat.getExtras(n);
159            assertFalse(extras.containsKey(EXTRA_COLORIZED));
160        }
161    }
162
163    @Test
164    public void testNotificationActionBuilder_copiesRemoteInputs() throws Throwable {
165        NotificationCompat.Action a = newActionBuilder()
166                .addRemoteInput(new RemoteInput("a", "b", null, false, null, null)).build();
167
168        NotificationCompat.Action aCopy = new NotificationCompat.Action.Builder(a).build();
169
170        assertSame(a.getRemoteInputs()[0], aCopy.getRemoteInputs()[0]);
171    }
172
173    @Test
174    public void testNotificationActionBuilder_copiesAllowGeneratedReplies() throws Throwable {
175        NotificationCompat.Action a = newActionBuilder()
176                .setAllowGeneratedReplies(true).build();
177
178        NotificationCompat.Action aCopy = new NotificationCompat.Action.Builder(a).build();
179
180        assertEquals(a.getAllowGeneratedReplies(), aCopy.getAllowGeneratedReplies());
181    }
182
183    @Test
184    public void testNotificationActionBuilder_defaultAllowGeneratedRepliesTrue() throws Throwable {
185        NotificationCompat.Action a = newActionBuilder().build();
186
187        assertTrue(a.getAllowGeneratedReplies());
188    }
189
190    @Test
191    public void testNotificationAction_defaultAllowGeneratedRepliesTrue() throws Throwable {
192        NotificationCompat.Action a = new NotificationCompat.Action(0, null, null);
193
194        assertTrue(a.getAllowGeneratedReplies());
195    }
196
197    @Test
198    public void testNotificationActionBuilder_setAllowGeneratedRepliesFalse() throws Throwable {
199        NotificationCompat.Action a = newActionBuilder()
200                .setAllowGeneratedReplies(false).build();
201
202        assertFalse(a.getAllowGeneratedReplies());
203    }
204
205    @SdkSuppress(minSdkVersion = 17)
206    @TargetApi(17)
207    @Test
208    public void testNotificationWearableExtenderAction_setAllowGeneratedRepliesTrue()
209            throws Throwable {
210        NotificationCompat.Action a = newActionBuilder()
211                .setAllowGeneratedReplies(true).build();
212        NotificationCompat.WearableExtender extender = new NotificationCompat.WearableExtender()
213                .addAction(a);
214        Notification notification = newNotificationBuilder().extend(extender).build();
215        assertTrue(new NotificationCompat.WearableExtender(notification).getActions().get(0)
216                .getAllowGeneratedReplies());
217    }
218
219    @SdkSuppress(minSdkVersion = 17)
220    @TargetApi(17)
221    @Test
222    public void testNotificationWearableExtenderAction_setAllowGeneratedRepliesFalse()
223            throws Throwable {
224        NotificationCompat.Action a = newActionBuilder()
225                .setAllowGeneratedReplies(false).build();
226        NotificationCompat.WearableExtender extender = new NotificationCompat.WearableExtender()
227                .addAction(a);
228        Notification notification = newNotificationBuilder().extend(extender).build();
229        assertFalse(new NotificationCompat.WearableExtender(notification).getActions().get(0)
230                .getAllowGeneratedReplies());
231    }
232
233
234    @SdkSuppress(maxSdkVersion = 16)
235    @SmallTest
236    @Test
237    public void testNotificationWearableExtenderAction_noActions()
238            throws Throwable {
239        NotificationCompat.Action a = newActionBuilder()
240                .setAllowGeneratedReplies(true).build();
241        NotificationCompat.WearableExtender extender = new NotificationCompat.WearableExtender()
242                .addAction(a);
243        Notification notification = newNotificationBuilder().extend(extender).build();
244        assertTrue(new NotificationCompat.WearableExtender(notification).getActions().size() == 0);
245    }
246
247    @Test
248    public void testNotificationActionBuilder_setDataOnlyRemoteInput() throws Throwable {
249        NotificationCompat.Action a = newActionBuilder()
250                .addRemoteInput(newDataOnlyRemoteInput()).build();
251        RemoteInput[] textInputs = a.getRemoteInputs();
252        assertTrue(textInputs == null || textInputs.length == 0);
253        verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY);
254    }
255
256    @Test
257    public void testNotificationActionBuilder_setTextAndDataOnlyRemoteInput() throws Throwable {
258        NotificationCompat.Action a = newActionBuilder()
259                .addRemoteInput(newDataOnlyRemoteInput())
260                .addRemoteInput(newTextRemoteInput())
261                .build();
262
263        verifyRemoteInputArrayHasSingleResult(a.getRemoteInputs(), TEXT_RESULT_KEY);
264        verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY);
265    }
266
267    @Test
268    public void testMessage_setAndGetExtras() throws Throwable {
269        String extraKey = "extra_key";
270        CharSequence extraValue = "extra_value";
271        NotificationCompat.MessagingStyle.Message m =
272                new NotificationCompat.MessagingStyle.Message("text", 0 /*timestamp */, "sender");
273        m.getExtras().putCharSequence(extraKey, extraValue);
274        assertEquals(extraValue, m.getExtras().getCharSequence(extraKey));
275
276        ArrayList<NotificationCompat.MessagingStyle.Message> messages = new ArrayList<>(1);
277        messages.add(m);
278        Bundle[] bundleArray =
279                NotificationCompat.MessagingStyle.Message.getBundleArrayForMessages(messages);
280        assertEquals(1, bundleArray.length);
281        NotificationCompat.MessagingStyle.Message fromBundle =
282                NotificationCompat.MessagingStyle.Message.getMessageFromBundle(bundleArray[0]);
283        assertEquals(extraValue, fromBundle.getExtras().getCharSequence(extraKey));
284    }
285
286    @Test
287    public void testGetGroupAlertBehavior() throws Throwable {
288        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
289                .setGroupAlertBehavior(GROUP_ALERT_CHILDREN)
290                .build();
291        if (BuildCompat.isAtLeastO()) {
292            assertEquals(GROUP_ALERT_CHILDREN, NotificationCompat.getGroupAlertBehavior(n));
293        } else {
294            assertEquals(GROUP_ALERT_ALL, NotificationCompat.getGroupAlertBehavior(n));
295        }
296    }
297
298    @Test
299    public void testGroupAlertBehavior_mutesGroupNotifications() throws Throwable {
300        // valid between api 20, when groups were added, and api 25, the last to use sound
301        // and vibration from the notification itself
302
303        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
304                .setGroupAlertBehavior(GROUP_ALERT_CHILDREN)
305                .setVibrate(new long[] {235})
306                .setSound(Uri.EMPTY)
307                .setDefaults(DEFAULT_ALL)
308                .setGroup("grouped")
309                .setGroupSummary(true)
310                .build();
311
312        Notification n2 = new NotificationCompat.Builder(mActivityTestRule.getActivity())
313                .setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
314                .setVibrate(new long[] {235})
315                .setSound(Uri.EMPTY)
316                .setDefaults(DEFAULT_ALL)
317                .setGroup("grouped")
318                .setGroupSummary(false)
319                .build();
320
321        if (Build.VERSION.SDK_INT >= 20 && !BuildCompat.isAtLeastO()) {
322            assertNull(n.sound);
323            assertNull(n.vibrate);
324            assertTrue((n.defaults & DEFAULT_LIGHTS) != 0);
325            assertTrue((n.defaults & DEFAULT_SOUND) == 0);
326            assertTrue((n.defaults & DEFAULT_VIBRATE) == 0);
327
328            assertNull(n2.sound);
329            assertNull(n2.vibrate);
330            assertTrue((n2.defaults & DEFAULT_LIGHTS) != 0);
331            assertTrue((n2.defaults & DEFAULT_SOUND) == 0);
332            assertTrue((n2.defaults & DEFAULT_VIBRATE) == 0);
333        } else if (Build.VERSION.SDK_INT < 20) {
334            assertNotNull(n.sound);
335            assertNotNull(n.vibrate);
336            assertTrue((n.defaults & DEFAULT_LIGHTS) != 0);
337            assertTrue((n.defaults & DEFAULT_SOUND) != 0);
338            assertTrue((n.defaults & DEFAULT_VIBRATE) != 0);
339
340            assertNotNull(n2.sound);
341            assertNotNull(n2.vibrate);
342            assertTrue((n2.defaults & DEFAULT_LIGHTS) != 0);
343            assertTrue((n2.defaults & DEFAULT_SOUND) != 0);
344            assertTrue((n2.defaults & DEFAULT_VIBRATE) != 0);
345        }
346    }
347
348    @Test
349    public void testGroupAlertBehavior_doesNotMuteIncorrectGroupNotifications() throws Throwable {
350        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
351                .setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
352                .setVibrate(new long[] {235})
353                .setSound(Uri.EMPTY)
354                .setDefaults(DEFAULT_ALL)
355                .setGroup("grouped")
356                .setGroupSummary(true)
357                .build();
358
359        Notification n2 = new NotificationCompat.Builder(mActivityTestRule.getActivity())
360                .setGroupAlertBehavior(GROUP_ALERT_CHILDREN)
361                .setVibrate(new long[] {235})
362                .setSound(Uri.EMPTY)
363                .setDefaults(DEFAULT_ALL)
364                .setGroup("grouped")
365                .setGroupSummary(false)
366                .build();
367
368        Notification n3 = new NotificationCompat.Builder(mActivityTestRule.getActivity())
369                .setVibrate(new long[] {235})
370                .setSound(Uri.EMPTY)
371                .setDefaults(DEFAULT_ALL)
372                .setGroup("grouped")
373                .setGroupSummary(false)
374                .build();
375
376        if (Build.VERSION.SDK_INT >= 20 && !BuildCompat.isAtLeastO()) {
377            assertNotNull(n.sound);
378            assertNotNull(n.vibrate);
379            assertTrue((n.defaults & DEFAULT_LIGHTS) != 0);
380            assertTrue((n.defaults & DEFAULT_SOUND) != 0);
381            assertTrue((n.defaults & DEFAULT_VIBRATE) != 0);
382
383            assertNotNull(n2.sound);
384            assertNotNull(n2.vibrate);
385            assertTrue((n2.defaults & DEFAULT_LIGHTS) != 0);
386            assertTrue((n2.defaults & DEFAULT_SOUND) != 0);
387            assertTrue((n2.defaults & DEFAULT_VIBRATE) != 0);
388
389            assertNotNull(n3.sound);
390            assertNotNull(n3.vibrate);
391            assertTrue((n3.defaults & DEFAULT_LIGHTS) != 0);
392            assertTrue((n3.defaults & DEFAULT_SOUND) != 0);
393            assertTrue((n3.defaults & DEFAULT_VIBRATE) != 0);
394        }
395    }
396
397    @Test
398    public void testGroupAlertBehavior_doesNotMuteNonGroupNotifications() throws Throwable {
399        Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity())
400                .setGroupAlertBehavior(GROUP_ALERT_CHILDREN)
401                .setVibrate(new long[] {235})
402                .setSound(Uri.EMPTY)
403                .setDefaults(DEFAULT_ALL)
404                .setGroup(null)
405                .setGroupSummary(false)
406                .build();
407        if (!BuildCompat.isAtLeastO()) {
408            assertNotNull(n.sound);
409            assertNotNull(n.vibrate);
410            assertTrue((n.defaults & DEFAULT_LIGHTS) != 0);
411            assertTrue((n.defaults & DEFAULT_SOUND) != 0);
412            assertTrue((n.defaults & DEFAULT_VIBRATE) != 0);
413        }
414    }
415
416    private static RemoteInput newDataOnlyRemoteInput() {
417        return new RemoteInput.Builder(DATA_RESULT_KEY)
418            .setAllowFreeFormInput(false)
419            .setAllowDataType("mimeType", true)
420            .build();
421    }
422
423    private static RemoteInput newTextRemoteInput() {
424        return new RemoteInput.Builder(TEXT_RESULT_KEY).build();  // allowFreeForm defaults to true
425    }
426
427    private static void verifyRemoteInputArrayHasSingleResult(
428            RemoteInput[] remoteInputs, String expectedResultKey) {
429        assertTrue(remoteInputs != null && remoteInputs.length == 1);
430        assertEquals(expectedResultKey, remoteInputs[0].getResultKey());
431    }
432
433    private static NotificationCompat.Action.Builder newActionBuilder() {
434        return new NotificationCompat.Action.Builder(0, "title", null);
435    }
436
437    private NotificationCompat.Builder newNotificationBuilder() {
438        return new NotificationCompat.Builder(mContext)
439                .setSmallIcon(0)
440                .setContentTitle("title")
441                .setContentText("text");
442    }
443}
444