Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2008 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 android.app.cts;
     18 
     19 import static android.app.Notification.FLAG_BUBBLE;
     20 import static android.graphics.drawable.Icon.TYPE_ADAPTIVE_BITMAP;
     21 import static android.graphics.drawable.Icon.TYPE_RESOURCE;
     22 
     23 import android.app.Notification;
     24 import android.app.Notification.Action.Builder;
     25 import android.app.Notification.MessagingStyle;
     26 import android.app.Notification.MessagingStyle.Message;
     27 import android.app.NotificationChannel;
     28 import android.app.NotificationManager;
     29 import android.app.PendingIntent;
     30 import android.app.Person;
     31 import android.app.RemoteInput;
     32 import android.content.Context;
     33 import android.content.Intent;
     34 import android.graphics.Bitmap;
     35 import android.graphics.Canvas;
     36 import android.graphics.drawable.Icon;
     37 import android.net.Uri;
     38 import android.os.Build;
     39 import android.os.Parcel;
     40 import android.os.Parcelable;
     41 import androidx.annotation.Nullable;
     42 
     43 import android.os.StrictMode;
     44 import android.test.AndroidTestCase;
     45 import android.widget.RemoteViews;
     46 
     47 import java.util.ArrayList;
     48 import java.util.List;
     49 import java.util.function.Consumer;
     50 
     51 public class NotificationTest extends AndroidTestCase {
     52     private static final String TEXT_RESULT_KEY = "text";
     53     private static final String DATA_RESULT_KEY = "data";
     54     private static final String DATA_AND_TEXT_RESULT_KEY = "data and text";
     55 
     56     private Notification.Action mAction;
     57     private Notification mNotification;
     58     private Context mContext;
     59 
     60     private static final String TICKER_TEXT = "tickerText";
     61     private static final String CONTENT_TITLE = "contentTitle";
     62     private static final String CONTENT_TEXT = "contentText";
     63     private static final String URI_STRING = "uriString";
     64     private static final String ACTION_TITLE = "actionTitle";
     65     private static final int BUBBLE_HEIGHT = 300;
     66     private static final int BUBBLE_HEIGHT_RESID = 31415;
     67     private static final int TOLERANCE = 200;
     68     private static final long TIMEOUT = 4000;
     69     private static final NotificationChannel CHANNEL = new NotificationChannel("id", "name",
     70             NotificationManager.IMPORTANCE_HIGH);
     71     private static final String SHORTCUT_ID = "shortcutId";
     72     private static final String SETTING_TEXT = "work chats";
     73     private static final boolean ALLOW_SYS_GEN_CONTEXTUAL_ACTIONS = false;
     74 
     75     @Override
     76     protected void setUp() throws Exception {
     77         super.setUp();
     78         mContext = getContext();
     79         mNotification = new Notification();
     80     }
     81 
     82     public void testConstructor() {
     83         mNotification = null;
     84         mNotification = new Notification();
     85         assertNotNull(mNotification);
     86         assertTrue(System.currentTimeMillis() - mNotification.when < TOLERANCE);
     87 
     88         mNotification = null;
     89         final int notificationTime = 200;
     90         mNotification = new Notification(0, TICKER_TEXT, notificationTime);
     91         assertEquals(notificationTime, mNotification.when);
     92         assertEquals(0, mNotification.icon);
     93         assertEquals(TICKER_TEXT, mNotification.tickerText);
     94         assertEquals(0, mNotification.number);
     95     }
     96 
     97     public void testBuilderConstructor() {
     98         mNotification = new Notification.Builder(mContext, CHANNEL.getId()).build();
     99         assertEquals(CHANNEL.getId(), mNotification.getChannelId());
    100         assertEquals(Notification.BADGE_ICON_NONE, mNotification.getBadgeIconType());
    101         assertNull(mNotification.getShortcutId());
    102         assertEquals(Notification.GROUP_ALERT_ALL, mNotification.getGroupAlertBehavior());
    103         assertEquals((long) 0, mNotification.getTimeoutAfter());
    104     }
    105 
    106     public void testDescribeContents() {
    107         final int expected = 0;
    108         mNotification = new Notification();
    109         assertEquals(expected, mNotification.describeContents());
    110     }
    111 
    112     public void testCategories() {
    113         assertNotNull(Notification.CATEGORY_ALARM);
    114         assertNotNull(Notification.CATEGORY_CALL);
    115         assertNotNull(Notification.CATEGORY_EMAIL);
    116         assertNotNull(Notification.CATEGORY_ERROR);
    117         assertNotNull(Notification.CATEGORY_EVENT);
    118         assertNotNull(Notification.CATEGORY_MESSAGE);
    119         assertNotNull(Notification.CATEGORY_NAVIGATION);
    120         assertNotNull(Notification.CATEGORY_PROGRESS);
    121         assertNotNull(Notification.CATEGORY_PROMO);
    122         assertNotNull(Notification.CATEGORY_RECOMMENDATION);
    123         assertNotNull(Notification.CATEGORY_REMINDER);
    124         assertNotNull(Notification.CATEGORY_SERVICE);
    125         assertNotNull(Notification.CATEGORY_SOCIAL);
    126         assertNotNull(Notification.CATEGORY_STATUS);
    127         assertNotNull(Notification.CATEGORY_SYSTEM);
    128         assertNotNull(Notification.CATEGORY_TRANSPORT);
    129     }
    130 
    131     public void testWriteToParcel() {
    132         Notification.BubbleMetadata bubble = makeBubbleMetadata();
    133         mNotification = new Notification.Builder(mContext, CHANNEL.getId())
    134                 .setBadgeIconType(Notification.BADGE_ICON_SMALL)
    135                 .setShortcutId(SHORTCUT_ID)
    136                 .setTimeoutAfter(TIMEOUT)
    137                 .setSettingsText(SETTING_TEXT)
    138                 .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
    139                 .setBubbleMetadata(bubble)
    140                 .setAllowSystemGeneratedContextualActions(ALLOW_SYS_GEN_CONTEXTUAL_ACTIONS)
    141                 .build();
    142         mNotification.icon = 0;
    143         mNotification.number = 1;
    144         final Intent intent = new Intent();
    145         final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
    146         mNotification.contentIntent = pendingIntent;
    147         final Intent deleteIntent = new Intent();
    148         final PendingIntent delPendingIntent = PendingIntent.getBroadcast(
    149                 mContext, 0, deleteIntent, 0);
    150         mNotification.deleteIntent = delPendingIntent;
    151         mNotification.tickerText = TICKER_TEXT;
    152 
    153         final RemoteViews contentView = new RemoteViews(mContext.getPackageName(),
    154                 android.R.layout.simple_list_item_1);
    155         mNotification.contentView = contentView;
    156         mNotification.defaults = 0;
    157         mNotification.flags = 0;
    158         final Uri uri = Uri.parse(URI_STRING);
    159         mNotification.sound = uri;
    160         mNotification.audioStreamType = 0;
    161         final long[] longArray = { 1l, 2l, 3l };
    162         mNotification.vibrate = longArray;
    163         mNotification.ledARGB = 0;
    164         mNotification.ledOnMS = 0;
    165         mNotification.ledOffMS = 0;
    166         mNotification.iconLevel = 0;
    167 
    168         Parcel parcel = Parcel.obtain();
    169         mNotification.writeToParcel(parcel, 0);
    170         parcel.setDataPosition(0);
    171         // Test Notification(Parcel)
    172         Notification result = new Notification(parcel);
    173         assertEquals(mNotification.icon, result.icon);
    174         assertEquals(mNotification.when, result.when);
    175         assertEquals(mNotification.number, result.number);
    176         assertNotNull(result.contentIntent);
    177         assertNotNull(result.deleteIntent);
    178         assertEquals(mNotification.tickerText, result.tickerText);
    179         assertNotNull(result.contentView);
    180         assertEquals(mNotification.defaults, result.defaults);
    181         assertEquals(mNotification.flags, result.flags);
    182         assertNotNull(result.sound);
    183         assertEquals(mNotification.audioStreamType, result.audioStreamType);
    184         assertEquals(mNotification.vibrate[0], result.vibrate[0]);
    185         assertEquals(mNotification.vibrate[1], result.vibrate[1]);
    186         assertEquals(mNotification.vibrate[2], result.vibrate[2]);
    187         assertEquals(mNotification.ledARGB, result.ledARGB);
    188         assertEquals(mNotification.ledOnMS, result.ledOnMS);
    189         assertEquals(mNotification.ledOffMS, result.ledOffMS);
    190         assertEquals(mNotification.iconLevel, result.iconLevel);
    191         assertEquals(mNotification.getShortcutId(), result.getShortcutId());
    192         assertEquals(mNotification.getBadgeIconType(), result.getBadgeIconType());
    193         assertEquals(mNotification.getTimeoutAfter(), result.getTimeoutAfter());
    194         assertEquals(mNotification.getChannelId(), result.getChannelId());
    195         assertEquals(mNotification.getSettingsText(), result.getSettingsText());
    196         assertEquals(mNotification.getGroupAlertBehavior(), result.getGroupAlertBehavior());
    197         assertNotNull(result.getBubbleMetadata());
    198         assertEquals(mNotification.getAllowSystemGeneratedContextualActions(),
    199                 result.getAllowSystemGeneratedContextualActions());
    200 
    201         mNotification.contentIntent = null;
    202         parcel = Parcel.obtain();
    203         mNotification.writeToParcel(parcel, 0);
    204         parcel.setDataPosition(0);
    205         result = new Notification(parcel);
    206         assertNull(result.contentIntent);
    207 
    208         mNotification.deleteIntent = null;
    209         parcel = Parcel.obtain();
    210         mNotification.writeToParcel(parcel, 0);
    211         parcel.setDataPosition(0);
    212         result = new Notification(parcel);
    213         assertNull(result.deleteIntent);
    214 
    215         mNotification.tickerText = null;
    216         parcel = Parcel.obtain();
    217         mNotification.writeToParcel(parcel, 0);
    218         parcel.setDataPosition(0);
    219         result = new Notification(parcel);
    220         assertNull(result.tickerText);
    221 
    222         mNotification.contentView = null;
    223         parcel = Parcel.obtain();
    224         mNotification.writeToParcel(parcel, 0);
    225         parcel.setDataPosition(0);
    226         result = new Notification(parcel);
    227         assertNull(result.contentView);
    228 
    229         mNotification.sound = null;
    230         parcel = Parcel.obtain();
    231         mNotification.writeToParcel(parcel, 0);
    232         parcel.setDataPosition(0);
    233         result = new Notification(parcel);
    234         assertNull(result.sound);
    235     }
    236 
    237     public void testColorizeNotification() {
    238         mNotification = new Notification.Builder(mContext, "channel_id")
    239                 .setSmallIcon(1)
    240                 .setContentTitle(CONTENT_TITLE)
    241                 .setColorized(true)
    242                 .build();
    243 
    244         assertTrue(mNotification.extras.getBoolean(Notification.EXTRA_COLORIZED));
    245     }
    246 
    247     public void testBuilder() {
    248         final Intent intent = new Intent();
    249         final PendingIntent contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
    250         Notification.BubbleMetadata bubble = makeBubbleMetadata();
    251         mNotification = new Notification.Builder(mContext, CHANNEL.getId())
    252                 .setSmallIcon(1)
    253                 .setContentTitle(CONTENT_TITLE)
    254                 .setContentText(CONTENT_TEXT)
    255                 .setContentIntent(contentIntent)
    256                 .setBadgeIconType(Notification.BADGE_ICON_SMALL)
    257                 .setShortcutId(SHORTCUT_ID)
    258                 .setTimeoutAfter(TIMEOUT)
    259                 .setSettingsText(SETTING_TEXT)
    260                 .setGroupAlertBehavior(Notification.GROUP_ALERT_SUMMARY)
    261                 .setBubbleMetadata(bubble)
    262                 .setAllowSystemGeneratedContextualActions(ALLOW_SYS_GEN_CONTEXTUAL_ACTIONS)
    263                 .build();
    264         assertEquals(CONTENT_TEXT, mNotification.extras.getString(Notification.EXTRA_TEXT));
    265         assertEquals(CONTENT_TITLE, mNotification.extras.getString(Notification.EXTRA_TITLE));
    266         assertEquals(1, mNotification.icon);
    267         assertEquals(contentIntent, mNotification.contentIntent);
    268         assertEquals(CHANNEL.getId(), mNotification.getChannelId());
    269         assertEquals(Notification.BADGE_ICON_SMALL, mNotification.getBadgeIconType());
    270         assertEquals(SHORTCUT_ID, mNotification.getShortcutId());
    271         assertEquals(TIMEOUT, mNotification.getTimeoutAfter());
    272         assertEquals(SETTING_TEXT, mNotification.getSettingsText());
    273         assertEquals(Notification.GROUP_ALERT_SUMMARY, mNotification.getGroupAlertBehavior());
    274         assertEquals(bubble, mNotification.getBubbleMetadata());
    275         assertEquals(ALLOW_SYS_GEN_CONTEXTUAL_ACTIONS,
    276                 mNotification.getAllowSystemGeneratedContextualActions());
    277     }
    278 
    279     public void testBuilder_getStyle() {
    280         MessagingStyle ms = new MessagingStyle(new Person.Builder().setName("Test name").build());
    281         Notification.Builder builder = new Notification.Builder(mContext, CHANNEL.getId());
    282 
    283         builder.setStyle(ms);
    284 
    285         assertEquals(ms, builder.getStyle());
    286     }
    287 
    288     public void testActionBuilder() {
    289         final Intent intent = new Intent();
    290         final PendingIntent actionIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
    291         mAction = null;
    292         mAction = new Notification.Action.Builder(0, ACTION_TITLE, actionIntent).build();
    293         assertEquals(ACTION_TITLE, mAction.title);
    294         assertEquals(actionIntent, mAction.actionIntent);
    295         assertEquals(true, mAction.getAllowGeneratedReplies());
    296     }
    297 
    298     public void testNotification_addPerson() {
    299         String name = "name";
    300         String key = "key";
    301         String uri = "name:name";
    302         Person person = new Person.Builder()
    303                 .setName(name)
    304                 .setIcon(Icon.createWithResource(mContext, 1))
    305                 .setKey(key)
    306                 .setUri(uri)
    307                 .build();
    308         mNotification = new Notification.Builder(mContext, CHANNEL.getId())
    309                 .setSmallIcon(1)
    310                 .setContentTitle(CONTENT_TITLE)
    311                 .addPerson(person)
    312                 .build();
    313 
    314         ArrayList<Person> restoredPeople = mNotification.extras.getParcelableArrayList(
    315                 Notification.EXTRA_PEOPLE_LIST);
    316         assertNotNull(restoredPeople);
    317         Person restoredPerson = restoredPeople.get(0);
    318         assertNotNull(restoredPerson);
    319         assertNotNull(restoredPerson.getIcon());
    320         assertEquals(name, restoredPerson.getName());
    321         assertEquals(key, restoredPerson.getKey());
    322         assertEquals(uri, restoredPerson.getUri());
    323     }
    324 
    325     public void testNotification_MessagingStyle_people() {
    326         String name = "name";
    327         String key = "key";
    328         String uri = "name:name";
    329         Person user = new Person.Builder()
    330                 .setName(name)
    331                 .setIcon(Icon.createWithResource(mContext, 1))
    332                 .setKey(key)
    333                 .setUri(uri)
    334                 .build();
    335         Person participant = new Person.Builder().setName("sender").build();
    336         Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(user)
    337                 .addMessage("text", 0, participant)
    338                 .addMessage(new Message("text 2", 0, participant));
    339         mNotification = new Notification.Builder(mContext, CHANNEL.getId())
    340                 .setSmallIcon(1)
    341                 .setStyle(messagingStyle)
    342                 .build();
    343 
    344         Person restoredPerson = mNotification.extras.getParcelable(
    345                 Notification.EXTRA_MESSAGING_PERSON);
    346         assertNotNull(restoredPerson);
    347         assertNotNull(restoredPerson.getIcon());
    348         assertEquals(name, restoredPerson.getName());
    349         assertEquals(key, restoredPerson.getKey());
    350         assertEquals(uri, restoredPerson.getUri());
    351         assertNotNull(mNotification.extras.getParcelableArray(Notification.EXTRA_MESSAGES));
    352     }
    353 
    354 
    355     public void testMessagingStyle_historicMessages() {
    356         Message referenceMessage = new Message("historic text", 0, "historic sender");
    357         Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
    358                 .addMessage("text", 0, "sender")
    359                 .addMessage(new Message("image", 0, "sender")
    360                         .setData("image/png", Uri.parse("http://example.com/image.png")))
    361                 .addHistoricMessage(referenceMessage)
    362                 .setConversationTitle("title");
    363         mNotification = new Notification.Builder(mContext, CHANNEL.getId())
    364                 .setSmallIcon(1)
    365                 .setContentTitle(CONTENT_TITLE)
    366                 .setStyle(messagingStyle)
    367                 .build();
    368 
    369         List<Message> historicMessages = messagingStyle.getHistoricMessages();
    370         assertNotNull(historicMessages);
    371         assertEquals(1, historicMessages.size());
    372         Message message = historicMessages.get(0);
    373         assertEquals(referenceMessage, message);
    374 
    375         assertNotNull(
    376                 mNotification.extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES));
    377     }
    378 
    379     public void testMessagingStyle_isGroupConversation() {
    380         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
    381         Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
    382                 .setGroupConversation(true)
    383                 .setConversationTitle("test conversation title");
    384         Notification notification = new Notification.Builder(mContext, "test id")
    385                 .setSmallIcon(1)
    386                 .setContentTitle("test title")
    387                 .setStyle(messagingStyle)
    388                 .build();
    389 
    390         assertTrue(messagingStyle.isGroupConversation());
    391         assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
    392     }
    393 
    394     public void testMessagingStyle_isGroupConversation_noConversationTitle() {
    395         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
    396         Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
    397                 .setGroupConversation(true)
    398                 .setConversationTitle(null);
    399         Notification notification = new Notification.Builder(mContext, "test id")
    400                 .setSmallIcon(1)
    401                 .setContentTitle("test title")
    402                 .setStyle(messagingStyle)
    403                 .build();
    404 
    405         assertTrue(messagingStyle.isGroupConversation());
    406         assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
    407     }
    408 
    409     public void testMessagingStyle_isGroupConversation_withConversationTitle_legacy() {
    410         // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
    411         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
    412         Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
    413                 .setGroupConversation(false)
    414                 .setConversationTitle("test conversation title");
    415         Notification notification = new Notification.Builder(mContext, "test id")
    416                 .setSmallIcon(1)
    417                 .setContentTitle("test title")
    418                 .setStyle(messagingStyle)
    419                 .build();
    420 
    421         assertTrue(messagingStyle.isGroupConversation());
    422         assertFalse(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
    423     }
    424 
    425     public void testMessagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
    426         // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
    427         mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
    428         Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
    429                 .setGroupConversation(true)
    430                 .setConversationTitle(null);
    431         Notification notification = new Notification.Builder(mContext, "test id")
    432                 .setSmallIcon(1)
    433                 .setContentTitle("test title")
    434                 .setStyle(messagingStyle)
    435                 .build();
    436 
    437         assertFalse(messagingStyle.isGroupConversation());
    438         assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
    439     }
    440 
    441     public void testMessagingStyle_getUser() {
    442         Person user = new Person.Builder().setName("Test name").build();
    443 
    444         MessagingStyle messagingStyle = new MessagingStyle(user);
    445 
    446         assertEquals(user, messagingStyle.getUser());
    447     }
    448 
    449     public void testMessagingStyle_getConversationTitle() {
    450         final String title = "test conversation title";
    451         Person user = new Person.Builder().setName("Test name").build();
    452         MessagingStyle messagingStyle = new MessagingStyle(user).setConversationTitle(title);
    453 
    454         Notification notification = new Notification.Builder(mContext, CHANNEL.getId())
    455                 .setSmallIcon(1)
    456                 .setContentTitle(CONTENT_TITLE)
    457                 .setStyle(messagingStyle)
    458                 .build();
    459 
    460         assertEquals(title, messagingStyle.getConversationTitle());
    461         assertEquals(title, notification.extras.getString(Notification.EXTRA_CONVERSATION_TITLE));
    462     }
    463 
    464     public void testMessage() {
    465         String senderName = "Test name";
    466         Person sender = new Person.Builder().setName(senderName).build();
    467         String text = "Test message";
    468         long timestamp = 400;
    469 
    470         Message message = new Message(text, timestamp, sender);
    471 
    472         assertEquals(text, message.getText());
    473         assertEquals(timestamp, message.getTimestamp());
    474         assertEquals(sender, message.getSenderPerson());
    475         assertEquals(senderName, message.getSender());
    476     }
    477 
    478     public void testMessageData() {
    479         Person sender = new Person.Builder().setName("Test name").build();
    480         String text = "Test message";
    481         long timestamp = 400;
    482         Message message = new Message(text, timestamp, sender);
    483 
    484         String mimeType = "image/png";
    485         Uri uri = Uri.parse("http://example.com/image.png");
    486         message.setData(mimeType, uri);
    487 
    488         assertEquals(mimeType, message.getDataMimeType());
    489         assertEquals(uri, message.getDataUri());
    490     }
    491 
    492     public void testToString() {
    493         mNotification = new Notification();
    494         assertNotNull(mNotification.toString());
    495         mNotification = null;
    496     }
    497 
    498     public void testNotificationActionBuilder_setDataOnlyRemoteInput() throws Throwable {
    499         Notification.Action a = newActionBuilder()
    500                 .addRemoteInput(newDataOnlyRemoteInput()).build();
    501         RemoteInput[] textInputs = a.getRemoteInputs();
    502         assertTrue(textInputs == null || textInputs.length == 0);
    503         verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY);
    504     }
    505 
    506     public void testNotificationActionBuilder_setTextAndDataOnlyRemoteInput() throws Throwable {
    507         Notification.Action a = newActionBuilder()
    508                 .addRemoteInput(newDataOnlyRemoteInput())
    509                 .addRemoteInput(newTextRemoteInput())
    510                 .build();
    511 
    512         verifyRemoteInputArrayHasSingleResult(a.getRemoteInputs(), TEXT_RESULT_KEY);
    513         verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY);
    514     }
    515 
    516     public void testNotificationActionBuilder_setTextAndDataOnlyAndBothRemoteInput()
    517             throws Throwable {
    518         Notification.Action a = newActionBuilder()
    519                 .addRemoteInput(newDataOnlyRemoteInput())
    520                 .addRemoteInput(newTextRemoteInput())
    521                 .addRemoteInput(newTextAndDataRemoteInput())
    522                 .build();
    523 
    524         assertTrue(a.getRemoteInputs() != null && a.getRemoteInputs().length == 2);
    525         assertEquals(TEXT_RESULT_KEY, a.getRemoteInputs()[0].getResultKey());
    526         assertFalse(a.getRemoteInputs()[0].isDataOnly());
    527         assertEquals(DATA_AND_TEXT_RESULT_KEY, a.getRemoteInputs()[1].getResultKey());
    528         assertFalse(a.getRemoteInputs()[1].isDataOnly());
    529 
    530         verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY);
    531         assertTrue(a.getDataOnlyRemoteInputs()[0].isDataOnly());
    532     }
    533 
    534     public void testAction_builder_hasDefault() {
    535         Notification.Action action = makeNotificationAction(null);
    536         assertEquals(Notification.Action.SEMANTIC_ACTION_NONE, action.getSemanticAction());
    537     }
    538 
    539     public void testAction_builder_setSemanticAction() {
    540         Notification.Action action = makeNotificationAction(
    541                 builder -> builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_REPLY));
    542         assertEquals(Notification.Action.SEMANTIC_ACTION_REPLY, action.getSemanticAction());
    543     }
    544 
    545     public void testAction_builder_contextualAction_nullIcon() {
    546         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    547         Notification.Action.Builder builder =
    548                 new Notification.Action.Builder(null /* icon */, "title", pendingIntent)
    549                 .setContextual(true);
    550         try {
    551             builder.build();
    552             fail("Creating a semantic Action with a null icon should cause a NullPointerException");
    553         } catch (NullPointerException e) {
    554             // Expected
    555         }
    556     }
    557 
    558     public void testAction_builder_contextualAction_nullIntent() {
    559         Notification.Action.Builder builder =
    560                 new Notification.Action.Builder(0 /* icon */, "title", null /* intent */)
    561                 .setContextual(true);
    562         try {
    563             builder.build();
    564             fail("Creating a semantic Action with a null PendingIntent should cause a "
    565                     + "NullPointerException");
    566         } catch (NullPointerException e) {
    567             // Expected
    568         }
    569     }
    570 
    571     public void testAction_parcel() {
    572         Notification.Action action = writeAndReadParcelable(
    573                 makeNotificationAction(builder -> {
    574                     builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_ARCHIVE);
    575                     builder.setAllowGeneratedReplies(true);
    576                 }));
    577 
    578         assertEquals(Notification.Action.SEMANTIC_ACTION_ARCHIVE, action.getSemanticAction());
    579         assertTrue(action.getAllowGeneratedReplies());
    580     }
    581 
    582     public void testAction_clone() {
    583         Notification.Action action = makeNotificationAction(
    584                 builder -> builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_DELETE));
    585         assertEquals(
    586                 Notification.Action.SEMANTIC_ACTION_DELETE,
    587                 action.clone().getSemanticAction());
    588     }
    589 
    590     public void testBuildStrictMode() {
    591         try {
    592             StrictMode.setThreadPolicy(
    593                     new StrictMode.ThreadPolicy.Builder().detectAll().penaltyDeath().build());
    594             Notification.Action a = newActionBuilder()
    595                     .addRemoteInput(newDataOnlyRemoteInput())
    596                     .addRemoteInput(newTextRemoteInput())
    597                     .addRemoteInput(newTextAndDataRemoteInput())
    598                     .build();
    599             Notification.Builder b = new Notification.Builder(mContext, "id")
    600                     .setStyle(new Notification.BigTextStyle().setBigContentTitle("Big content"))
    601                     .setContentTitle("title")
    602                     .setActions(a);
    603 
    604             b.build();
    605         } finally {
    606             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
    607         }
    608     }
    609 
    610     public void testGetAllowSystemGeneratedContextualActions_trueByDefault() {
    611         Notification notification = new Notification.Builder(mContext, CHANNEL.getId()).build();
    612         assertTrue(notification.getAllowSystemGeneratedContextualActions());
    613     }
    614 
    615     public void testGetAllowSystemGeneratedContextualActions() {
    616         Notification notification = new Notification.Builder(mContext, CHANNEL.getId())
    617                 .setAllowSystemGeneratedContextualActions(false)
    618                 .build();
    619         assertFalse(notification.getAllowSystemGeneratedContextualActions());
    620     }
    621 
    622     public void testBubbleMetadataBuilder() {
    623         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    624         PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    625         Icon icon = Icon.createWithResource(mContext, 1);
    626         Notification.BubbleMetadata.Builder metadataBuilder =
    627                 new Notification.BubbleMetadata.Builder()
    628                 .setDesiredHeight(BUBBLE_HEIGHT)
    629                 .setIcon(icon)
    630                 .setIntent(bubbleIntent)
    631                 .setDeleteIntent(deleteIntent);
    632 
    633         Notification.BubbleMetadata data = metadataBuilder.build();
    634         assertEquals(BUBBLE_HEIGHT, data.getDesiredHeight());
    635         assertEquals(icon, data.getIcon());
    636         assertEquals(bubbleIntent, data.getIntent());
    637         assertEquals(deleteIntent, data.getDeleteIntent());
    638         assertFalse(data.isNotificationSuppressed());
    639         assertFalse(data.getAutoExpandBubble());
    640     }
    641 
    642     public void testBubbleMetadata_parcel() {
    643         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    644         PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    645         Icon icon = Icon.createWithResource(mContext, 1);
    646         Notification.BubbleMetadata metadata =
    647                 new Notification.BubbleMetadata.Builder()
    648                         .setDesiredHeight(BUBBLE_HEIGHT)
    649                         .setAutoExpandBubble(true)
    650                         .setSuppressNotification(true)
    651                         .setIcon(icon)
    652                         .setIntent(bubbleIntent)
    653                         .setDeleteIntent(deleteIntent)
    654                         .build();
    655 
    656         writeAndReadParcelable(metadata);
    657         assertEquals(BUBBLE_HEIGHT, metadata.getDesiredHeight());
    658         assertEquals(icon, metadata.getIcon());
    659         assertEquals(bubbleIntent, metadata.getIntent());
    660         assertEquals(deleteIntent, metadata.getDeleteIntent());
    661         assertTrue(metadata.getAutoExpandBubble());
    662         assertTrue(metadata.isNotificationSuppressed());
    663     }
    664 
    665     public void testBubbleMetadata_parcelResId() {
    666         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    667         Icon icon = Icon.createWithResource(mContext, 1);
    668         Notification.BubbleMetadata metadata =
    669                 new Notification.BubbleMetadata.Builder()
    670                         .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
    671                         .setIcon(icon)
    672                         .setIntent(bubbleIntent)
    673                         .build();
    674         writeAndReadParcelable(metadata);
    675         assertEquals(BUBBLE_HEIGHT_RESID, metadata.getDesiredHeightResId());
    676         assertEquals(icon, metadata.getIcon());
    677         assertEquals(bubbleIntent, metadata.getIntent());
    678         assertFalse(metadata.getAutoExpandBubble());
    679         assertFalse(metadata.isNotificationSuppressed());
    680     }
    681 
    682     public void testBubbleMetadataBuilder_throwForNoIntent() {
    683         Icon icon = Icon.createWithResource(mContext, 1);
    684         Notification.BubbleMetadata.Builder metadataBuilder =
    685                 new Notification.BubbleMetadata.Builder()
    686                 .setDesiredHeight(BUBBLE_HEIGHT)
    687                 .setIcon(icon);
    688         try {
    689             metadataBuilder.build();
    690             fail("Should have thrown IllegalStateException, no pending intent");
    691         } catch (IllegalStateException e) {
    692             // expected
    693         }
    694     }
    695 
    696     public void testBubbleMetadataBuilder_throwForNoIcon() {
    697         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    698         Notification.BubbleMetadata.Builder metadataBuilder =
    699                 new Notification.BubbleMetadata.Builder()
    700                 .setDesiredHeight(BUBBLE_HEIGHT)
    701                 .setIntent(bubbleIntent);
    702         try {
    703             metadataBuilder.build();
    704             fail("Should have thrown IllegalStateException, no icon");
    705         } catch (IllegalStateException e) {
    706             // expected
    707         }
    708     }
    709 
    710     public void testBubbleMetadataBuilder_throwForBitmapIcon() {
    711         Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
    712         new Canvas(b).drawColor(0xffff0000);
    713         Icon icon = Icon.createWithBitmap(b);
    714 
    715         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    716         try {
    717             Notification.BubbleMetadata.Builder metadataBuilder =
    718                     new Notification.BubbleMetadata.Builder()
    719                             .setIcon(icon)
    720                             .setIntent(bubbleIntent);
    721             fail("Should have thrown IllegalArgumentException, invalid icon type (bitmap)");
    722         } catch (IllegalArgumentException e) {
    723             // expected
    724         }
    725     }
    726 
    727     public void testBubbleMetadataBuilder_noThrowForAdaptiveBitmapIcon() {
    728         Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
    729         new Canvas(b).drawColor(0xffff0000);
    730         Icon icon = Icon.createWithAdaptiveBitmap(b);
    731 
    732         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    733         Notification.BubbleMetadata.Builder metadataBuilder =
    734                 new Notification.BubbleMetadata.Builder()
    735                         .setIcon(icon)
    736                         .setIntent(bubbleIntent);
    737         Notification.BubbleMetadata metadata = metadataBuilder.build();
    738         assertNotNull(metadata.getIcon());
    739         assertEquals(TYPE_ADAPTIVE_BITMAP, metadata.getIcon().getType());
    740     }
    741 
    742     public void testBubbleMetadataBuilder_noThrowForNonBitmapIcon() {
    743         Icon icon = Icon.createWithResource(mContext, R.drawable.ic_android);
    744 
    745         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    746         Notification.BubbleMetadata.Builder metadataBuilder =
    747                 new Notification.BubbleMetadata.Builder()
    748                         .setIcon(icon)
    749                         .setIntent(bubbleIntent);
    750         Notification.BubbleMetadata metadata = metadataBuilder.build();
    751         assertNotNull(metadata.getIcon());
    752         assertEquals(TYPE_RESOURCE, metadata.getIcon().getType());
    753     }
    754 
    755     public void testBubbleMetadataBuilder_replaceHeightRes() {
    756         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    757         PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    758         Icon icon = Icon.createWithResource(mContext, 1);
    759         Notification.BubbleMetadata.Builder metadataBuilder =
    760                 new Notification.BubbleMetadata.Builder()
    761                         .setDesiredHeight(BUBBLE_HEIGHT)
    762                         .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
    763                         .setIcon(icon)
    764                         .setIntent(bubbleIntent)
    765                         .setDeleteIntent(deleteIntent);
    766 
    767         Notification.BubbleMetadata data = metadataBuilder.build();
    768         // Desired height should be cleared
    769         assertEquals(0, data.getDesiredHeight());
    770         // Res id should be used
    771         assertEquals(BUBBLE_HEIGHT_RESID, data.getDesiredHeightResId());
    772     }
    773 
    774     public void testBubbleMetadataBuilder_replaceHeightDp() {
    775         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    776         PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    777         Icon icon = Icon.createWithResource(mContext, 1);
    778         Notification.BubbleMetadata.Builder metadataBuilder =
    779                 new Notification.BubbleMetadata.Builder()
    780                         .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
    781                         .setDesiredHeight(BUBBLE_HEIGHT)
    782                         .setIcon(icon)
    783                         .setIntent(bubbleIntent)
    784                         .setDeleteIntent(deleteIntent);
    785 
    786         Notification.BubbleMetadata data = metadataBuilder.build();
    787         // Desired height should be used
    788         assertEquals(BUBBLE_HEIGHT, data.getDesiredHeight());
    789         // Res id should be cleared
    790         assertEquals(0, data.getDesiredHeightResId());
    791     }
    792 
    793     public void testFlagBubble() {
    794         Notification n = new Notification();
    795         assertFalse((n.flags & FLAG_BUBBLE) != 0);
    796         n.flags |= FLAG_BUBBLE;
    797         assertTrue((n.flags & FLAG_BUBBLE) != 0);
    798     }
    799 
    800     private static RemoteInput newDataOnlyRemoteInput() {
    801         return new RemoteInput.Builder(DATA_RESULT_KEY)
    802             .setAllowFreeFormInput(false)
    803             .setAllowDataType("mimeType", true)
    804             .build();
    805     }
    806 
    807     private static RemoteInput newTextAndDataRemoteInput() {
    808         return new RemoteInput.Builder(DATA_AND_TEXT_RESULT_KEY)
    809             .setAllowDataType("mimeType", true)
    810             .build();  // allowFreeForm defaults to true
    811     }
    812 
    813     private static RemoteInput newTextRemoteInput() {
    814         return new RemoteInput.Builder(TEXT_RESULT_KEY).build();  // allowFreeForm defaults to true
    815     }
    816 
    817     private static void verifyRemoteInputArrayHasSingleResult(
    818             RemoteInput[] remoteInputs, String expectedResultKey) {
    819         assertTrue(remoteInputs != null && remoteInputs.length == 1);
    820         assertEquals(expectedResultKey, remoteInputs[0].getResultKey());
    821     }
    822 
    823     private static Notification.Action.Builder newActionBuilder() {
    824         return new Notification.Action.Builder(0, "title", null);
    825     }
    826 
    827     /**
    828      * Writes an arbitrary {@link Parcelable} into a {@link Parcel} using its writeToParcel
    829      * method before reading it out again to check that it was sent properly.
    830      */
    831     private static <T extends Parcelable> T writeAndReadParcelable(T original) {
    832         Parcel p = Parcel.obtain();
    833         p.writeParcelable(original, /* flags */ 0);
    834         p.setDataPosition(0);
    835         return p.readParcelable(/* classLoader */ null);
    836     }
    837 
    838     /**
    839      * Creates a Notification.Action by mocking initial dependencies and then applying
    840      * transformations if they're defined.
    841      */
    842     private Notification.Action makeNotificationAction(
    843             @Nullable Consumer<Builder> transformation) {
    844         Notification.Action.Builder actionBuilder =
    845             new Notification.Action.Builder(null, "Test Title", null);
    846         if (transformation != null) {
    847             transformation.accept(actionBuilder);
    848         }
    849         return actionBuilder.build();
    850     }
    851 
    852     private Notification.BubbleMetadata makeBubbleMetadata() {
    853         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
    854 
    855         return new Notification.BubbleMetadata.Builder()
    856                         .setIntent(bubbleIntent)
    857                         .setIcon(Icon.createWithResource(mContext, 1))
    858                         .setDesiredHeight(BUBBLE_HEIGHT)
    859                         .build();
    860     }
    861 }
    862